Fix WB promotion popup
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts. \r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009, 2010 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 \r
77 #if __GNUC__\r
78 #include <errno.h>\r
79 #include <string.h>\r
80 #endif\r
81 \r
82 #include "common.h"\r
83 #include "frontend.h"\r
84 #include "backend.h"\r
85 #include "winboard.h"\r
86 #include "moves.h"\r
87 #include "wclipbrd.h"\r
88 #include "woptions.h"\r
89 #include "wsockerr.h"\r
90 #include "defaults.h"\r
91 #include "help.h"\r
92 #include "wsnap.h"\r
93 \r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
95 \r
96   int myrandom(void);\r
97   void mysrandom(unsigned int seed);\r
98 \r
99 extern int whiteFlag, blackFlag;\r
100 Boolean flipClock = FALSE;\r
101 extern HANDLE chatHandle[];\r
102 extern int ics_type;\r
103 \r
104 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
105 VOID NewVariantPopup(HWND hwnd);\r
106 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
107                    /*char*/int promoChar));\r
108 void DisplayMove P((int moveNumber));\r
109 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
110 void ChatPopUp P((char *s));\r
111 typedef struct {\r
112   ChessSquare piece;  \r
113   POINT pos;      /* window coordinates of current pos */\r
114   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
115   POINT from;     /* board coordinates of the piece's orig pos */\r
116   POINT to;       /* board coordinates of the piece's new pos */\r
117 } AnimInfo;\r
118 \r
119 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
120 \r
121 typedef struct {\r
122   POINT start;    /* window coordinates of start pos */\r
123   POINT pos;      /* window coordinates of current pos */\r
124   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
125   POINT from;     /* board coordinates of the piece's orig pos */\r
126 } DragInfo;\r
127 \r
128 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
129 \r
130 typedef struct {\r
131   POINT sq[2];    /* board coordinates of from, to squares */\r
132 } HighlightInfo;\r
133 \r
134 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
135 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
136 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
137 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
138 \r
139 typedef struct { // [HGM] atomic\r
140   int fromX, fromY, toX, toY, radius;\r
141 } ExplodeInfo;\r
142 \r
143 static ExplodeInfo explodeInfo;\r
144 \r
145 /* Window class names */\r
146 char szAppName[] = "WinBoard";\r
147 char szConsoleName[] = "WBConsole";\r
148 \r
149 /* Title bar text */\r
150 char szTitle[] = "WinBoard";\r
151 char szConsoleTitle[] = "I C S Interaction";\r
152 \r
153 char *programName;\r
154 char *settingsFileName;\r
155 Boolean saveSettingsOnExit;\r
156 char installDir[MSG_SIZ];\r
157 int errorExitStatus;\r
158 \r
159 BoardSize boardSize;\r
160 Boolean chessProgram;\r
161 //static int boardX, boardY;\r
162 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
163 int squareSize, lineGap, minorSize;\r
164 static int winW, winH;\r
165 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
166 static int logoHeight = 0;\r
167 static char messageText[MESSAGE_TEXT_MAX];\r
168 static int clockTimerEvent = 0;\r
169 static int loadGameTimerEvent = 0;\r
170 static int analysisTimerEvent = 0;\r
171 static DelayedEventCallback delayedTimerCallback;\r
172 static int delayedTimerEvent = 0;\r
173 static int buttonCount = 2;\r
174 char *icsTextMenuString;\r
175 char *icsNames;\r
176 char *firstChessProgramNames;\r
177 char *secondChessProgramNames;\r
178 \r
179 #define PALETTESIZE 256\r
180 \r
181 HINSTANCE hInst;          /* current instance */\r
182 Boolean alwaysOnTop = FALSE;\r
183 RECT boardRect;\r
184 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
185   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
186 HPALETTE hPal;\r
187 ColorClass currentColorClass;\r
188 \r
189 static HWND savedHwnd;\r
190 HWND hCommPort = NULL;    /* currently open comm port */\r
191 static HWND hwndPause;    /* pause button */\r
192 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
193 static HBRUSH lightSquareBrush, darkSquareBrush,\r
194   blackSquareBrush, /* [HGM] for band between board and holdings */\r
195   explodeBrush,     /* [HGM] atomic */\r
196   markerBrush,      /* [HGM] markers */\r
197   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
198 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
199 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
200 static HPEN gridPen = NULL;\r
201 static HPEN highlightPen = NULL;\r
202 static HPEN premovePen = NULL;\r
203 static NPLOGPALETTE pLogPal;\r
204 static BOOL paletteChanged = FALSE;\r
205 static HICON iconWhite, iconBlack, iconCurrent;\r
206 static int doingSizing = FALSE;\r
207 static int lastSizing = 0;\r
208 static int prevStderrPort;\r
209 static HBITMAP userLogo;\r
210 \r
211 static HBITMAP liteBackTexture = NULL;\r
212 static HBITMAP darkBackTexture = NULL;\r
213 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
214 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
215 static int backTextureSquareSize = 0;\r
216 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
217 \r
218 #if __GNUC__ && !defined(_winmajor)\r
219 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
220 #else\r
221 #if defined(_winmajor)\r
222 #define oldDialog (_winmajor < 4)\r
223 #else\r
224 #define oldDialog 0\r
225 #endif\r
226 #endif\r
227 \r
228 #define INTERNATIONAL\r
229 \r
230 #ifdef INTERNATIONAL\r
231 #  define _(s) T_(s)\r
232 #  define N_(s) s\r
233 #else\r
234 #  define _(s) s\r
235 #  define N_(s) s\r
236 #  define T_(s) s\r
237 #  define Translate(x, y)\r
238 #  define LoadLanguageFile(s)\r
239 #endif\r
240 \r
241 #ifdef INTERNATIONAL\r
242 \r
243 Boolean barbaric; // flag indicating if translation is needed\r
244 \r
245 // list of item numbers used in each dialog (used to alter language at run time)\r
246 \r
247 #define ABOUTBOX -1  /* not sure why these are needed */\r
248 #define ABOUTBOX2 -1\r
249 \r
250 int dialogItems[][40] = {\r
251 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
252 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
253   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
254 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, IDOK, IDCANCEL }, \r
255 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
256   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
257 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
258 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
259   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
260 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
261 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
262   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
263 { ABOUTBOX2, IDC_ChessBoard }, \r
264 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
265   OPT_GameListClose, IDC_GameListDoFilter }, \r
266 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
267 { DLG_Error, IDOK }, \r
268 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
269   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
270 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
271 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
272   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
273   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
274 { DLG_IndexNumber, IDC_Index }, \r
275 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
276 { DLG_TypeInName, IDOK, IDCANCEL }, \r
277 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
278   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
279 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
280   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
281   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
282   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
283   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
284   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
285   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
286 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
287   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
288   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
289   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
290   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
291   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
292   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
293   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
294   GPB_General, GPB_Alarm }, \r
295 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
296   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
297   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
298   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
299   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
300   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
301   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
302   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size }, \r
303 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
304   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
305   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
306   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
307   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
308   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
309   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
310   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
311   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
312 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
313   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
314   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,\r
315   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
316 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
317 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
318   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
319 { DLG_MoveHistory }, \r
320 { DLG_EvalGraph }, \r
321 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
322 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
323 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
324   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
325   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
326   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
327 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
328   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
329   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
330 { 0 }\r
331 };\r
332 \r
333 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
334 static int lastChecked;\r
335 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
336 extern int tinyLayout;\r
337 extern char * menuBarText[][10];\r
338 \r
339 void\r
340 LoadLanguageFile(char *name)\r
341 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
342     FILE *f;\r
343     int i=0, j=0, n=0, k;\r
344     char buf[MSG_SIZ];\r
345 \r
346     if(!name || name[0] == NULLCHAR) return;\r
347       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
348     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
349     if((f = fopen(buf, "r")) == NULL) return;\r
350     while((k = fgetc(f)) != EOF) {\r
351         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
352         languageBuf[i] = k;\r
353         if(k == '\n') {\r
354             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
355                 char *p;\r
356                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
357                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
358                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
359                         english[j] = languageBuf + n + 1; *p = 0;\r
360                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
361 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
362                     }\r
363                 }\r
364             }\r
365             n = i + 1;\r
366         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
367             switch(k) {\r
368               case 'n': k = '\n'; break;\r
369               case 'r': k = '\r'; break;\r
370               case 't': k = '\t'; break;\r
371             }\r
372             languageBuf[--i] = k;\r
373         }\r
374         i++;\r
375     }\r
376     fclose(f);\r
377     barbaric = (j != 0);\r
378     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
379 }\r
380 \r
381 char *\r
382 T_(char *s)\r
383 {   // return the translation of the given string\r
384     // efficiency can be improved a lot...\r
385     int i=0;\r
386 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
387     if(!barbaric) return s;\r
388     if(!s) return ""; // sanity\r
389     while(english[i]) {\r
390         if(!strcmp(s, english[i])) return foreign[i];\r
391         i++;\r
392     }\r
393     return s;\r
394 }\r
395 \r
396 void\r
397 Translate(HWND hDlg, int dialogID)\r
398 {   // translate all text items in the given dialog\r
399     int i=0, j, k;\r
400     char buf[MSG_SIZ], *s;\r
401     if(!barbaric) return;\r
402     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
403     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
404     GetWindowText( hDlg, buf, MSG_SIZ );\r
405     s = T_(buf);\r
406     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
407     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
408         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
409         if(strlen(buf) == 0) continue;\r
410         s = T_(buf);\r
411         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
412     }\r
413 }\r
414 \r
415 HMENU\r
416 TranslateOneMenu(int i, HMENU subMenu)\r
417 {\r
418     int j;\r
419     static MENUITEMINFO info;\r
420 \r
421     info.cbSize = sizeof(MENUITEMINFO);\r
422     info.fMask = MIIM_STATE | MIIM_TYPE;\r
423           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
424             char buf[MSG_SIZ];\r
425             info.dwTypeData = buf;\r
426             info.cch = sizeof(buf);\r
427             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
428             if(i < 10) {
429                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
430                 else menuText[i][j] = strdup(buf); // remember original on first change\r
431             }\r
432             if(buf[0] == NULLCHAR) continue;\r
433             info.dwTypeData = T_(buf);\r
434             info.cch = strlen(buf)+1;\r
435             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
436           }\r
437     return subMenu;\r
438 }\r
439 \r
440 void\r
441 TranslateMenus(int addLanguage)\r
442 {\r
443     int i;\r
444     WIN32_FIND_DATA fileData;\r
445     HANDLE hFind;\r
446 #define IDM_English 1895\r
447     if(1) {\r
448         HMENU mainMenu = GetMenu(hwndMain);\r
449         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
450           HMENU subMenu = GetSubMenu(mainMenu, i);\r
451           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
452                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
453           TranslateOneMenu(i, subMenu);\r
454         }\r
455         DrawMenuBar(hwndMain);\r
456     }\r
457 \r
458     if(!addLanguage) return;\r
459     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
460         HMENU mainMenu = GetMenu(hwndMain);\r
461         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
462         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
463         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
464         i = 0; lastChecked = IDM_English;\r
465         do {\r
466             char *p, *q = fileData.cFileName;\r
467             int checkFlag = MF_UNCHECKED;\r
468             languageFile[i] = strdup(q);\r
469             if(barbaric && !strcmp(oldLanguage, q)) {\r
470                 checkFlag = MF_CHECKED;\r
471                 lastChecked = IDM_English + i + 1;\r
472                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
473             }\r
474             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
475             p = strstr(fileData.cFileName, ".lng");\r
476             if(p) *p = 0;\r
477             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
478         } while(FindNextFile(hFind, &fileData));\r
479         FindClose(hFind);\r
480     }\r
481 }\r
482 \r
483 #endif\r
484 \r
485 typedef struct {\r
486   char *name;\r
487   int squareSize;\r
488   int lineGap;\r
489   int smallLayout;\r
490   int tinyLayout;\r
491   int cliWidth, cliHeight;\r
492 } SizeInfo;\r
493 \r
494 SizeInfo sizeInfo[] = \r
495 {\r
496   { "tiny",     21, 0, 1, 1, 0, 0 },\r
497   { "teeny",    25, 1, 1, 1, 0, 0 },\r
498   { "dinky",    29, 1, 1, 1, 0, 0 },\r
499   { "petite",   33, 1, 1, 1, 0, 0 },\r
500   { "slim",     37, 2, 1, 0, 0, 0 },\r
501   { "small",    40, 2, 1, 0, 0, 0 },\r
502   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
503   { "middling", 49, 2, 0, 0, 0, 0 },\r
504   { "average",  54, 2, 0, 0, 0, 0 },\r
505   { "moderate", 58, 3, 0, 0, 0, 0 },\r
506   { "medium",   64, 3, 0, 0, 0, 0 },\r
507   { "bulky",    72, 3, 0, 0, 0, 0 },\r
508   { "large",    80, 3, 0, 0, 0, 0 },\r
509   { "big",      87, 3, 0, 0, 0, 0 },\r
510   { "huge",     95, 3, 0, 0, 0, 0 },\r
511   { "giant",    108, 3, 0, 0, 0, 0 },\r
512   { "colossal", 116, 4, 0, 0, 0, 0 },\r
513   { "titanic",  129, 4, 0, 0, 0, 0 },\r
514   { NULL, 0, 0, 0, 0, 0, 0 }\r
515 };\r
516 \r
517 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
518 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
519 {\r
520   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL) },\r
521   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL) },\r
522   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL) },\r
523   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL) },\r
524   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL) },\r
525   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL) },\r
526   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL) },\r
527   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL) },\r
528   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL) },\r
529   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL) },\r
530   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL) },\r
531   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL) },\r
532   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL) },\r
533   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL) },\r
534   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL) },\r
535   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL) },\r
536   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL) },\r
537   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL) },\r
538 };\r
539 \r
540 MyFont *font[NUM_SIZES][NUM_FONTS];\r
541 \r
542 typedef struct {\r
543   char *label;\r
544   int id;\r
545   HWND hwnd;\r
546   WNDPROC wndproc;\r
547 } MyButtonDesc;\r
548 \r
549 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
550 #define N_BUTTONS 5\r
551 \r
552 MyButtonDesc buttonDesc[N_BUTTONS] =\r
553 {\r
554   {"<<", IDM_ToStart, NULL, NULL},\r
555   {"<", IDM_Backward, NULL, NULL},\r
556   {"P", IDM_Pause, NULL, NULL},\r
557   {">", IDM_Forward, NULL, NULL},\r
558   {">>", IDM_ToEnd, NULL, NULL},\r
559 };\r
560 \r
561 int tinyLayout = 0, smallLayout = 0;\r
562 #define MENU_BAR_ITEMS 9\r
563 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
564   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
565   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
566 };\r
567 \r
568 \r
569 MySound sounds[(int)NSoundClasses];\r
570 MyTextAttribs textAttribs[(int)NColorClasses];\r
571 \r
572 MyColorizeAttribs colorizeAttribs[] = {\r
573   { (COLORREF)0, 0, N_("Shout Text") },\r
574   { (COLORREF)0, 0, N_("SShout/CShout") },\r
575   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
576   { (COLORREF)0, 0, N_("Channel Text") },\r
577   { (COLORREF)0, 0, N_("Kibitz Text") },\r
578   { (COLORREF)0, 0, N_("Tell Text") },\r
579   { (COLORREF)0, 0, N_("Challenge Text") },\r
580   { (COLORREF)0, 0, N_("Request Text") },\r
581   { (COLORREF)0, 0, N_("Seek Text") },\r
582   { (COLORREF)0, 0, N_("Normal Text") },\r
583   { (COLORREF)0, 0, N_("None") }\r
584 };\r
585 \r
586 \r
587 \r
588 static char *commentTitle;\r
589 static char *commentText;\r
590 static int commentIndex;\r
591 static Boolean editComment = FALSE;\r
592 \r
593 \r
594 char errorTitle[MSG_SIZ];\r
595 char errorMessage[2*MSG_SIZ];\r
596 HWND errorDialog = NULL;\r
597 BOOLEAN moveErrorMessageUp = FALSE;\r
598 BOOLEAN consoleEcho = TRUE;\r
599 CHARFORMAT consoleCF;\r
600 COLORREF consoleBackgroundColor;\r
601 \r
602 char *programVersion;\r
603 \r
604 #define CPReal 1\r
605 #define CPComm 2\r
606 #define CPSock 3\r
607 #define CPRcmd 4\r
608 typedef int CPKind;\r
609 \r
610 typedef struct {\r
611   CPKind kind;\r
612   HANDLE hProcess;\r
613   DWORD pid;\r
614   HANDLE hTo;\r
615   HANDLE hFrom;\r
616   SOCKET sock;\r
617   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
618 } ChildProc;\r
619 \r
620 #define INPUT_SOURCE_BUF_SIZE 4096\r
621 \r
622 typedef struct _InputSource {\r
623   CPKind kind;\r
624   HANDLE hFile;\r
625   SOCKET sock;\r
626   int lineByLine;\r
627   HANDLE hThread;\r
628   DWORD id;\r
629   char buf[INPUT_SOURCE_BUF_SIZE];\r
630   char *next;\r
631   DWORD count;\r
632   int error;\r
633   InputCallback func;\r
634   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
635   VOIDSTAR closure;\r
636 } InputSource;\r
637 \r
638 InputSource *consoleInputSource;\r
639 \r
640 DCB dcb;\r
641 \r
642 /* forward */\r
643 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
644 VOID ConsoleCreate();\r
645 LRESULT CALLBACK\r
646   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
647 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
648 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
649 VOID ParseCommSettings(char *arg, DCB *dcb);\r
650 LRESULT CALLBACK\r
651   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
652 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
653 void ParseIcsTextMenu(char *icsTextMenuString);\r
654 VOID PopUpMoveDialog(char firstchar);\r
655 VOID PopUpNameDialog(char firstchar);\r
656 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
657 \r
658 /* [AS] */\r
659 int NewGameFRC();\r
660 int GameListOptions();\r
661 \r
662 int dummy; // [HGM] for obsolete args\r
663 \r
664 HWND hwndMain = NULL;        /* root window*/\r
665 HWND hwndConsole = NULL;\r
666 HWND commentDialog = NULL;\r
667 HWND moveHistoryDialog = NULL;\r
668 HWND evalGraphDialog = NULL;\r
669 HWND engineOutputDialog = NULL;\r
670 HWND gameListDialog = NULL;\r
671 HWND editTagsDialog = NULL;\r
672 \r
673 int commentUp = FALSE;\r
674 \r
675 WindowPlacement wpMain;\r
676 WindowPlacement wpConsole;\r
677 WindowPlacement wpComment;\r
678 WindowPlacement wpMoveHistory;\r
679 WindowPlacement wpEvalGraph;\r
680 WindowPlacement wpEngineOutput;\r
681 WindowPlacement wpGameList;\r
682 WindowPlacement wpTags;\r
683 \r
684 VOID EngineOptionsPopup(); // [HGM] settings\r
685 \r
686 VOID GothicPopUp(char *title, VariantClass variant);\r
687 /*\r
688  * Setting "frozen" should disable all user input other than deleting\r
689  * the window.  We do this while engines are initializing themselves.\r
690  */\r
691 static int frozen = 0;\r
692 static int oldMenuItemState[MENU_BAR_ITEMS];\r
693 void FreezeUI()\r
694 {\r
695   HMENU hmenu;\r
696   int i;\r
697 \r
698   if (frozen) return;\r
699   frozen = 1;\r
700   hmenu = GetMenu(hwndMain);\r
701   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
702     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
703   }\r
704   DrawMenuBar(hwndMain);\r
705 }\r
706 \r
707 /* Undo a FreezeUI */\r
708 void ThawUI()\r
709 {\r
710   HMENU hmenu;\r
711   int i;\r
712 \r
713   if (!frozen) return;\r
714   frozen = 0;\r
715   hmenu = GetMenu(hwndMain);\r
716   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
717     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
718   }\r
719   DrawMenuBar(hwndMain);\r
720 }\r
721 \r
722 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
723 \r
724 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
725 #ifdef JAWS\r
726 #include "jaws.c"\r
727 #else\r
728 #define JAWS_INIT\r
729 #define JAWS_ARGS\r
730 #define JAWS_ALT_INTERCEPT\r
731 #define JAWS_KB_NAVIGATION\r
732 #define JAWS_MENU_ITEMS\r
733 #define JAWS_SILENCE\r
734 #define JAWS_REPLAY\r
735 #define JAWS_ACCEL\r
736 #define JAWS_COPYRIGHT\r
737 #define JAWS_DELETE(X) X\r
738 #define SAYMACHINEMOVE()\r
739 #define SAY(X)\r
740 #endif\r
741 \r
742 /*---------------------------------------------------------------------------*\\r
743  *\r
744  * WinMain\r
745  *\r
746 \*---------------------------------------------------------------------------*/\r
747 \r
748 int APIENTRY\r
749 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
750         LPSTR lpCmdLine, int nCmdShow)\r
751 {\r
752   MSG msg;\r
753   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
754 //  INITCOMMONCONTROLSEX ex;\r
755 \r
756   debugFP = stderr;\r
757 \r
758   LoadLibrary("RICHED32.DLL");\r
759   consoleCF.cbSize = sizeof(CHARFORMAT);\r
760 \r
761   if (!InitApplication(hInstance)) {\r
762     return (FALSE);\r
763   }\r
764   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
765     return (FALSE);\r
766   }\r
767 \r
768   JAWS_INIT\r
769   TranslateMenus(1);\r
770 \r
771 //  InitCommonControlsEx(&ex);\r
772   InitCommonControls();\r
773 \r
774   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
775   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
776   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
777 \r
778   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
779 \r
780   while (GetMessage(&msg, /* message structure */\r
781                     NULL, /* handle of window receiving the message */\r
782                     0,    /* lowest message to examine */\r
783                     0))   /* highest message to examine */\r
784     {\r
785 \r
786       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
787         // [HGM] navigate: switch between all windows with tab\r
788         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
789         int i, currentElement = 0;\r
790 \r
791         // first determine what element of the chain we come from (if any)\r
792         if(appData.icsActive) {\r
793             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
794             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
795         }\r
796         if(engineOutputDialog && EngineOutputIsUp()) {\r
797             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
798             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
799         }\r
800         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
801             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
802         }\r
803         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
804         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
805         if(msg.hwnd == e1)                 currentElement = 2; else\r
806         if(msg.hwnd == e2)                 currentElement = 3; else\r
807         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
808         if(msg.hwnd == mh)                currentElement = 4; else\r
809         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
810         if(msg.hwnd == hText)  currentElement = 5; else\r
811         if(msg.hwnd == hInput) currentElement = 6; else\r
812         for (i = 0; i < N_BUTTONS; i++) {\r
813             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
814         }\r
815 \r
816         // determine where to go to\r
817         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
818           do {\r
819             currentElement = (currentElement + direction) % 7;\r
820             switch(currentElement) {\r
821                 case 0:\r
822                   h = hwndMain; break; // passing this case always makes the loop exit\r
823                 case 1:\r
824                   h = buttonDesc[0].hwnd; break; // could be NULL\r
825                 case 2:\r
826                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
827                   h = e1; break;\r
828                 case 3:\r
829                   if(!EngineOutputIsUp()) continue;\r
830                   h = e2; break;\r
831                 case 4:\r
832                   if(!MoveHistoryIsUp()) continue;\r
833                   h = mh; break;\r
834 //              case 6: // input to eval graph does not seem to get here!\r
835 //                if(!EvalGraphIsUp()) continue;\r
836 //                h = evalGraphDialog; break;\r
837                 case 5:\r
838                   if(!appData.icsActive) continue;\r
839                   SAY("display");\r
840                   h = hText; break;\r
841                 case 6:\r
842                   if(!appData.icsActive) continue;\r
843                   SAY("input");\r
844                   h = hInput; break;\r
845             }\r
846           } while(h == 0);\r
847 \r
848           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
849           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
850           SetFocus(h);\r
851 \r
852           continue; // this message now has been processed\r
853         }\r
854       }\r
855 \r
856       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
857           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
858           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
859           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
860           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
861           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
862           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
863           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
864           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
865           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
866         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
867         for(i=0; i<MAX_CHAT; i++) \r
868             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
869                 done = 1; break;\r
870         }\r
871         if(done) continue; // [HGM] chat: end patch\r
872         TranslateMessage(&msg); /* Translates virtual key codes */\r
873         DispatchMessage(&msg);  /* Dispatches message to window */\r
874       }\r
875     }\r
876 \r
877 \r
878   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
879 }\r
880 \r
881 /*---------------------------------------------------------------------------*\\r
882  *\r
883  * Initialization functions\r
884  *\r
885 \*---------------------------------------------------------------------------*/\r
886 \r
887 void\r
888 SetUserLogo()\r
889 {   // update user logo if necessary\r
890     static char oldUserName[MSG_SIZ], *curName;\r
891 \r
892     if(appData.autoLogo) {\r
893           curName = UserName();\r
894           if(strcmp(curName, oldUserName)) {\r
895             snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
896                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
897                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
898                 if(userLogo == NULL)\r
899                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
900           }\r
901     }\r
902 }\r
903 \r
904 BOOL\r
905 InitApplication(HINSTANCE hInstance)\r
906 {\r
907   WNDCLASS wc;\r
908 \r
909   /* Fill in window class structure with parameters that describe the */\r
910   /* main window. */\r
911 \r
912   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
913   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
914   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
915   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
916   wc.hInstance     = hInstance;         /* Owner of this class */\r
917   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
918   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
919   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
920   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
921   wc.lpszClassName = szAppName;                 /* Name to register as */\r
922 \r
923   /* Register the window class and return success/failure code. */\r
924   if (!RegisterClass(&wc)) return FALSE;\r
925 \r
926   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
927   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
928   wc.cbClsExtra    = 0;\r
929   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
930   wc.hInstance     = hInstance;\r
931   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
932   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
933   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
934   wc.lpszMenuName  = NULL;\r
935   wc.lpszClassName = szConsoleName;\r
936 \r
937   if (!RegisterClass(&wc)) return FALSE;\r
938   return TRUE;\r
939 }\r
940 \r
941 \r
942 /* Set by InitInstance, used by EnsureOnScreen */\r
943 int screenHeight, screenWidth;\r
944 \r
945 void\r
946 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
947 {\r
948 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
949   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
950   if (*x > screenWidth - 32) *x = 0;\r
951   if (*y > screenHeight - 32) *y = 0;\r
952   if (*x < minX) *x = minX;\r
953   if (*y < minY) *y = minY;\r
954 }\r
955 \r
956 BOOL\r
957 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
958 {\r
959   HWND hwnd; /* Main window handle. */\r
960   int ibs;\r
961   WINDOWPLACEMENT wp;\r
962   char *filepart;\r
963 \r
964   hInst = hInstance;    /* Store instance handle in our global variable */\r
965   programName = szAppName;\r
966 \r
967   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
968     *filepart = NULLCHAR;\r
969   } else {\r
970     GetCurrentDirectory(MSG_SIZ, installDir);\r
971   }\r
972   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
973   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
974   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
975   /* xboard, and older WinBoards, controlled the move sound with the\r
976      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
977      always turn the option on (so that the backend will call us),\r
978      then let the user turn the sound off by setting it to silence if\r
979      desired.  To accommodate old winboard.ini files saved by old\r
980      versions of WinBoard, we also turn off the sound if the option\r
981      was initially set to false. [HGM] taken out of InitAppData */\r
982   if (!appData.ringBellAfterMoves) {\r
983     sounds[(int)SoundMove].name = strdup("");\r
984     appData.ringBellAfterMoves = TRUE;\r
985   }\r
986   if (appData.debugMode) {\r
987     debugFP = fopen(appData.nameOfDebugFile, "w");\r
988     setbuf(debugFP, NULL);\r
989   }\r
990 \r
991   LoadLanguageFile(appData.language);\r
992 \r
993   InitBackEnd1();\r
994 \r
995 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
996 //  InitEngineUCI( installDir, &second );\r
997 \r
998   /* Create a main window for this application instance. */\r
999   hwnd = CreateWindow(szAppName, szTitle,\r
1000                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1001                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1002                       NULL, NULL, hInstance, NULL);\r
1003   hwndMain = hwnd;\r
1004 \r
1005   /* If window could not be created, return "failure" */\r
1006   if (!hwnd) {\r
1007     return (FALSE);\r
1008   }\r
1009 \r
1010   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1011   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
1012       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1013 \r
1014       if (first.programLogo == NULL && appData.debugMode) {\r
1015           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
1016       }\r
1017   } else if(appData.autoLogo) {\r
1018       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
1019         char buf[MSG_SIZ];\r
1020           snprintf(buf, MSG_SIZ, "%s/logo.bmp", appData.firstDirectory);\r
1021         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
1022       }\r
1023   }\r
1024 \r
1025   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
1026       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1027 \r
1028       if (second.programLogo == NULL && appData.debugMode) {\r
1029           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
1030       }\r
1031   } else if(appData.autoLogo) {\r
1032       char buf[MSG_SIZ];\r
1033       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
1034         snprintf(buf, MSG_SIZ, "logos\\%s.bmp", appData.icsHost);\r
1035         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1036       } else\r
1037       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
1038         snprintf(buf, MSG_SIZ, "%s\\logo.bmp", appData.secondDirectory);\r
1039         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
1040       }\r
1041   }\r
1042 \r
1043   SetUserLogo();\r
1044 \r
1045   iconWhite = LoadIcon(hInstance, "icon_white");\r
1046   iconBlack = LoadIcon(hInstance, "icon_black");\r
1047   iconCurrent = iconWhite;\r
1048   InitDrawingColors();\r
1049   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1050   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1051   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1052     /* Compute window size for each board size, and use the largest\r
1053        size that fits on this screen as the default. */\r
1054     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1055     if (boardSize == (BoardSize)-1 &&\r
1056         winH <= screenHeight\r
1057            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1058         && winW <= screenWidth) {\r
1059       boardSize = (BoardSize)ibs;\r
1060     }\r
1061   }\r
1062 \r
1063   InitDrawingSizes(boardSize, 0);\r
1064   InitMenuChecks();\r
1065   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1066 \r
1067   /* [AS] Load textures if specified */\r
1068   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1069   \r
1070   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1071       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1072       liteBackTextureMode = appData.liteBackTextureMode;\r
1073 \r
1074       if (liteBackTexture == NULL && appData.debugMode) {\r
1075           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1076       }\r
1077   }\r
1078   \r
1079   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1080       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1081       darkBackTextureMode = appData.darkBackTextureMode;\r
1082 \r
1083       if (darkBackTexture == NULL && appData.debugMode) {\r
1084           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1085       }\r
1086   }\r
1087 \r
1088   mysrandom( (unsigned) time(NULL) );\r
1089 \r
1090   /* [AS] Restore layout */\r
1091   if( wpMoveHistory.visible ) {\r
1092       MoveHistoryPopUp();\r
1093   }\r
1094 \r
1095   if( wpEvalGraph.visible ) {\r
1096       EvalGraphPopUp();\r
1097   }\r
1098 \r
1099   if( wpEngineOutput.visible ) {\r
1100       EngineOutputPopUp();\r
1101   }\r
1102 \r
1103   /* Make the window visible; update its client area; and return "success" */\r
1104   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1105   wp.length = sizeof(WINDOWPLACEMENT);\r
1106   wp.flags = 0;\r
1107   wp.showCmd = nCmdShow;\r
1108   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1109   wp.rcNormalPosition.left = wpMain.x;\r
1110   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1111   wp.rcNormalPosition.top = wpMain.y;\r
1112   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1113   SetWindowPlacement(hwndMain, &wp);\r
1114 \r
1115   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1116 \r
1117   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1118                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1119 \r
1120   if (hwndConsole) {\r
1121 #if AOT_CONSOLE\r
1122     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1123                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1124 #endif\r
1125     ShowWindow(hwndConsole, nCmdShow);\r
1126     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
1127       char buf[MSG_SIZ], *p = buf, *q;\r
1128         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
1129       do {\r
1130         q = strchr(p, ';');\r
1131         if(q) *q++ = 0;\r
1132         if(*p) ChatPopUp(p);\r
1133       } while(p=q);\r
1134     }\r
1135     SetActiveWindow(hwndConsole);\r
1136   }\r
1137   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1138   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1139 \r
1140   return TRUE;\r
1141 \r
1142 }\r
1143 \r
1144 VOID\r
1145 InitMenuChecks()\r
1146 {\r
1147   HMENU hmenu = GetMenu(hwndMain);\r
1148 \r
1149   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1150                         MF_BYCOMMAND|((appData.icsActive &&\r
1151                                        *appData.icsCommPort != NULLCHAR) ?\r
1152                                       MF_ENABLED : MF_GRAYED));\r
1153   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1154                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1155                                      MF_CHECKED : MF_UNCHECKED));\r
1156 }\r
1157 \r
1158 //---------------------------------------------------------------------------------------------------------\r
1159 \r
1160 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1161 #define XBOARD FALSE\r
1162 \r
1163 #define OPTCHAR "/"\r
1164 #define SEPCHAR "="\r
1165 \r
1166 #include "args.h"\r
1167 \r
1168 // front-end part of option handling\r
1169 \r
1170 VOID\r
1171 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1172 {\r
1173   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1174   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1175   DeleteDC(hdc);\r
1176   lf->lfWidth = 0;\r
1177   lf->lfEscapement = 0;\r
1178   lf->lfOrientation = 0;\r
1179   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1180   lf->lfItalic = mfp->italic;\r
1181   lf->lfUnderline = mfp->underline;\r
1182   lf->lfStrikeOut = mfp->strikeout;\r
1183   lf->lfCharSet = mfp->charset;\r
1184   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1185   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1186   lf->lfQuality = DEFAULT_QUALITY;\r
1187   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1188     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1189 }\r
1190 \r
1191 void\r
1192 CreateFontInMF(MyFont *mf)\r
1193\r
1194   LFfromMFP(&mf->lf, &mf->mfp);\r
1195   if (mf->hf) DeleteObject(mf->hf);\r
1196   mf->hf = CreateFontIndirect(&mf->lf);\r
1197 }\r
1198 \r
1199 // [HGM] This platform-dependent table provides the location for storing the color info\r
1200 void *\r
1201 colorVariable[] = {\r
1202   &whitePieceColor, \r
1203   &blackPieceColor, \r
1204   &lightSquareColor,\r
1205   &darkSquareColor, \r
1206   &highlightSquareColor,\r
1207   &premoveHighlightColor,\r
1208   NULL,\r
1209   &consoleBackgroundColor,\r
1210   &appData.fontForeColorWhite,\r
1211   &appData.fontBackColorWhite,\r
1212   &appData.fontForeColorBlack,\r
1213   &appData.fontBackColorBlack,\r
1214   &appData.evalHistColorWhite,\r
1215   &appData.evalHistColorBlack,\r
1216   &appData.highlightArrowColor,\r
1217 };\r
1218 \r
1219 /* Command line font name parser.  NULL name means do nothing.\r
1220    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1221    For backward compatibility, syntax without the colon is also\r
1222    accepted, but font names with digits in them won't work in that case.\r
1223 */\r
1224 VOID\r
1225 ParseFontName(char *name, MyFontParams *mfp)\r
1226 {\r
1227   char *p, *q;\r
1228   if (name == NULL) return;\r
1229   p = name;\r
1230   q = strchr(p, ':');\r
1231   if (q) {\r
1232     if (q - p >= sizeof(mfp->faceName))\r
1233       ExitArgError(_("Font name too long:"), name);\r
1234     memcpy(mfp->faceName, p, q - p);\r
1235     mfp->faceName[q - p] = NULLCHAR;\r
1236     p = q + 1;\r
1237   } else {\r
1238     q = mfp->faceName;\r
1239     while (*p && !isdigit(*p)) {\r
1240       *q++ = *p++;\r
1241       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1242         ExitArgError(_("Font name too long:"), name);\r
1243     }\r
1244     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1245     *q = NULLCHAR;\r
1246   }\r
1247   if (!*p) ExitArgError(_("Font point size missing:"), name);\r
1248   mfp->pointSize = (float) atof(p);\r
1249   mfp->bold = (strchr(p, 'b') != NULL);\r
1250   mfp->italic = (strchr(p, 'i') != NULL);\r
1251   mfp->underline = (strchr(p, 'u') != NULL);\r
1252   mfp->strikeout = (strchr(p, 's') != NULL);\r
1253   mfp->charset = DEFAULT_CHARSET;\r
1254   q = strchr(p, 'c');\r
1255   if (q)\r
1256     mfp->charset = (BYTE) atoi(q+1);\r
1257 }\r
1258 \r
1259 void\r
1260 ParseFont(char *name, int number)\r
1261 { // wrapper to shield back-end from 'font'\r
1262   ParseFontName(name, &font[boardSize][number]->mfp);\r
1263 }\r
1264 \r
1265 void\r
1266 SetFontDefaults()\r
1267 { // in WB  we have a 2D array of fonts; this initializes their description\r
1268   int i, j;\r
1269   /* Point font array elements to structures and\r
1270      parse default font names */\r
1271   for (i=0; i<NUM_FONTS; i++) {\r
1272     for (j=0; j<NUM_SIZES; j++) {\r
1273       font[j][i] = &fontRec[j][i];\r
1274       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1275     }\r
1276   }\r
1277 }\r
1278 \r
1279 void\r
1280 CreateFonts()\r
1281 { // here we create the actual fonts from the selected descriptions\r
1282   int i, j;\r
1283   for (i=0; i<NUM_FONTS; i++) {\r
1284     for (j=0; j<NUM_SIZES; j++) {\r
1285       CreateFontInMF(font[j][i]);\r
1286     }\r
1287   }\r
1288 }\r
1289 /* Color name parser.\r
1290    X version accepts X color names, but this one\r
1291    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1292 COLORREF\r
1293 ParseColorName(char *name)\r
1294 {\r
1295   int red, green, blue, count;\r
1296   char buf[MSG_SIZ];\r
1297 \r
1298   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1299   if (count != 3) {\r
1300     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1301       &red, &green, &blue);\r
1302   }\r
1303   if (count != 3) {\r
1304     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1305     DisplayError(buf, 0);\r
1306     return RGB(0, 0, 0);\r
1307   }\r
1308   return PALETTERGB(red, green, blue);\r
1309 }\r
1310 \r
1311 void\r
1312 ParseColor(int n, char *name)\r
1313 { // for WinBoard the color is an int, which needs to be derived from the string\r
1314   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1315 }\r
1316 \r
1317 void\r
1318 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1319 {\r
1320   char *e = argValue;\r
1321   int eff = 0;\r
1322 \r
1323   while (*e) {\r
1324     if (*e == 'b')      eff |= CFE_BOLD;\r
1325     else if (*e == 'i') eff |= CFE_ITALIC;\r
1326     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1327     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1328     else if (*e == '#' || isdigit(*e)) break;\r
1329     e++;\r
1330   }\r
1331   *effects = eff;\r
1332   *color   = ParseColorName(e);\r
1333 }\r
1334 \r
1335 void\r
1336 ParseTextAttribs(ColorClass cc, char *s)\r
1337 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1338     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1339     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1340 }\r
1341 \r
1342 void\r
1343 ParseBoardSize(void *addr, char *name)\r
1344 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1345   BoardSize bs = SizeTiny;\r
1346   while (sizeInfo[bs].name != NULL) {\r
1347     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1348         *(BoardSize *)addr = bs;\r
1349         return;\r
1350     }\r
1351     bs++;\r
1352   }\r
1353   ExitArgError(_("Unrecognized board size value"), name);\r
1354 }\r
1355 \r
1356 void\r
1357 LoadAllSounds()\r
1358 { // [HGM] import name from appData first\r
1359   ColorClass cc;\r
1360   SoundClass sc;\r
1361   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1362     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1363     textAttribs[cc].sound.data = NULL;\r
1364     MyLoadSound(&textAttribs[cc].sound);\r
1365   }\r
1366   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1367     textAttribs[cc].sound.name = strdup("");\r
1368     textAttribs[cc].sound.data = NULL;\r
1369   }\r
1370   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1371     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1372     sounds[sc].data = NULL;\r
1373     MyLoadSound(&sounds[sc]);\r
1374   }\r
1375 }\r
1376 \r
1377 void\r
1378 SetCommPortDefaults()\r
1379 {\r
1380    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1381   dcb.DCBlength = sizeof(DCB);\r
1382   dcb.BaudRate = 9600;\r
1383   dcb.fBinary = TRUE;\r
1384   dcb.fParity = FALSE;\r
1385   dcb.fOutxCtsFlow = FALSE;\r
1386   dcb.fOutxDsrFlow = FALSE;\r
1387   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1388   dcb.fDsrSensitivity = FALSE;\r
1389   dcb.fTXContinueOnXoff = TRUE;\r
1390   dcb.fOutX = FALSE;\r
1391   dcb.fInX = FALSE;\r
1392   dcb.fNull = FALSE;\r
1393   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1394   dcb.fAbortOnError = FALSE;\r
1395   dcb.ByteSize = 7;\r
1396   dcb.Parity = SPACEPARITY;\r
1397   dcb.StopBits = ONESTOPBIT;\r
1398 }\r
1399 \r
1400 // [HGM] args: these three cases taken out to stay in front-end\r
1401 void\r
1402 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1403 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1404         // while the curent board size determines the element. This system should be ported to XBoard.\r
1405         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1406         int bs;\r
1407         for (bs=0; bs<NUM_SIZES; bs++) {\r
1408           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1409           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1410           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1411             ad->argName, mfp->faceName, mfp->pointSize,\r
1412             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1413             mfp->bold ? "b" : "",\r
1414             mfp->italic ? "i" : "",\r
1415             mfp->underline ? "u" : "",\r
1416             mfp->strikeout ? "s" : "",\r
1417             (int)mfp->charset);\r
1418         }\r
1419       }\r
1420 \r
1421 void\r
1422 ExportSounds()\r
1423 { // [HGM] copy the names from the internal WB variables to appData\r
1424   ColorClass cc;\r
1425   SoundClass sc;\r
1426   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1427     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1428   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1429     (&appData.soundMove)[sc] = sounds[sc].name;\r
1430 }\r
1431 \r
1432 void\r
1433 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1434 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1435         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1436         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1437           (ta->effects & CFE_BOLD) ? "b" : "",\r
1438           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1439           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1440           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1441           (ta->effects) ? " " : "",\r
1442           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1443       }\r
1444 \r
1445 void\r
1446 SaveColor(FILE *f, ArgDescriptor *ad)\r
1447 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1448         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1449         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1450           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1451 }\r
1452 \r
1453 void\r
1454 SaveBoardSize(FILE *f, char *name, void *addr)\r
1455 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1456   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1457 }\r
1458 \r
1459 void\r
1460 ParseCommPortSettings(char *s)\r
1461 { // wrapper to keep dcb from back-end\r
1462   ParseCommSettings(s, &dcb);\r
1463 }\r
1464 \r
1465 void\r
1466 GetWindowCoords()\r
1467 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1468   GetActualPlacement(hwndMain, &wpMain);\r
1469   GetActualPlacement(hwndConsole, &wpConsole);\r
1470   GetActualPlacement(commentDialog, &wpComment);\r
1471   GetActualPlacement(editTagsDialog, &wpTags);\r
1472   GetActualPlacement(gameListDialog, &wpGameList);\r
1473   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1474   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1475   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1476 }\r
1477 \r
1478 void\r
1479 PrintCommPortSettings(FILE *f, char *name)\r
1480 { // wrapper to shield back-end from DCB\r
1481       PrintCommSettings(f, name, &dcb);\r
1482 }\r
1483 \r
1484 int\r
1485 MySearchPath(char *installDir, char *name, char *fullname)\r
1486 {\r
1487   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1488   if(name[0]== '%') {\r
1489     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1490     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1491       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1492       *strchr(buf, '%') = 0;\r
1493       strcat(fullname, getenv(buf));\r
1494       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1495     }\r
1496     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1497     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1498     return (int) strlen(fullname);\r
1499   }\r
1500   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1501 }\r
1502 \r
1503 int\r
1504 MyGetFullPathName(char *name, char *fullname)\r
1505 {\r
1506   char *dummy;\r
1507   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1508 }\r
1509 \r
1510 int\r
1511 MainWindowUp()\r
1512 { // [HGM] args: allows testing if main window is realized from back-end\r
1513   return hwndMain != NULL;\r
1514 }\r
1515 \r
1516 void\r
1517 PopUpStartupDialog()\r
1518 {\r
1519     FARPROC lpProc;\r
1520     \r
1521     LoadLanguageFile(appData.language);\r
1522     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1523     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1524     FreeProcInstance(lpProc);\r
1525 }\r
1526 \r
1527 /*---------------------------------------------------------------------------*\\r
1528  *\r
1529  * GDI board drawing routines\r
1530  *\r
1531 \*---------------------------------------------------------------------------*/\r
1532 \r
1533 /* [AS] Draw square using background texture */\r
1534 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1535 {\r
1536     XFORM   x;\r
1537 \r
1538     if( mode == 0 ) {\r
1539         return; /* Should never happen! */\r
1540     }\r
1541 \r
1542     SetGraphicsMode( dst, GM_ADVANCED );\r
1543 \r
1544     switch( mode ) {\r
1545     case 1:\r
1546         /* Identity */\r
1547         break;\r
1548     case 2:\r
1549         /* X reflection */\r
1550         x.eM11 = -1.0;\r
1551         x.eM12 = 0;\r
1552         x.eM21 = 0;\r
1553         x.eM22 = 1.0;\r
1554         x.eDx = (FLOAT) dw + dx - 1;\r
1555         x.eDy = 0;\r
1556         dx = 0;\r
1557         SetWorldTransform( dst, &x );\r
1558         break;\r
1559     case 3:\r
1560         /* Y reflection */\r
1561         x.eM11 = 1.0;\r
1562         x.eM12 = 0;\r
1563         x.eM21 = 0;\r
1564         x.eM22 = -1.0;\r
1565         x.eDx = 0;\r
1566         x.eDy = (FLOAT) dh + dy - 1;\r
1567         dy = 0;\r
1568         SetWorldTransform( dst, &x );\r
1569         break;\r
1570     case 4:\r
1571         /* X/Y flip */\r
1572         x.eM11 = 0;\r
1573         x.eM12 = 1.0;\r
1574         x.eM21 = 1.0;\r
1575         x.eM22 = 0;\r
1576         x.eDx = (FLOAT) dx;\r
1577         x.eDy = (FLOAT) dy;\r
1578         dx = 0;\r
1579         dy = 0;\r
1580         SetWorldTransform( dst, &x );\r
1581         break;\r
1582     }\r
1583 \r
1584     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1585 \r
1586     x.eM11 = 1.0;\r
1587     x.eM12 = 0;\r
1588     x.eM21 = 0;\r
1589     x.eM22 = 1.0;\r
1590     x.eDx = 0;\r
1591     x.eDy = 0;\r
1592     SetWorldTransform( dst, &x );\r
1593 \r
1594     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1595 }\r
1596 \r
1597 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1598 enum {\r
1599     PM_WP = (int) WhitePawn, \r
1600     PM_WN = (int) WhiteKnight, \r
1601     PM_WB = (int) WhiteBishop, \r
1602     PM_WR = (int) WhiteRook, \r
1603     PM_WQ = (int) WhiteQueen, \r
1604     PM_WF = (int) WhiteFerz, \r
1605     PM_WW = (int) WhiteWazir, \r
1606     PM_WE = (int) WhiteAlfil, \r
1607     PM_WM = (int) WhiteMan, \r
1608     PM_WO = (int) WhiteCannon, \r
1609     PM_WU = (int) WhiteUnicorn, \r
1610     PM_WH = (int) WhiteNightrider, \r
1611     PM_WA = (int) WhiteAngel, \r
1612     PM_WC = (int) WhiteMarshall, \r
1613     PM_WAB = (int) WhiteCardinal, \r
1614     PM_WD = (int) WhiteDragon, \r
1615     PM_WL = (int) WhiteLance, \r
1616     PM_WS = (int) WhiteCobra, \r
1617     PM_WV = (int) WhiteFalcon, \r
1618     PM_WSG = (int) WhiteSilver, \r
1619     PM_WG = (int) WhiteGrasshopper, \r
1620     PM_WK = (int) WhiteKing,\r
1621     PM_BP = (int) BlackPawn, \r
1622     PM_BN = (int) BlackKnight, \r
1623     PM_BB = (int) BlackBishop, \r
1624     PM_BR = (int) BlackRook, \r
1625     PM_BQ = (int) BlackQueen, \r
1626     PM_BF = (int) BlackFerz, \r
1627     PM_BW = (int) BlackWazir, \r
1628     PM_BE = (int) BlackAlfil, \r
1629     PM_BM = (int) BlackMan,\r
1630     PM_BO = (int) BlackCannon, \r
1631     PM_BU = (int) BlackUnicorn, \r
1632     PM_BH = (int) BlackNightrider, \r
1633     PM_BA = (int) BlackAngel, \r
1634     PM_BC = (int) BlackMarshall, \r
1635     PM_BG = (int) BlackGrasshopper, \r
1636     PM_BAB = (int) BlackCardinal,\r
1637     PM_BD = (int) BlackDragon,\r
1638     PM_BL = (int) BlackLance,\r
1639     PM_BS = (int) BlackCobra,\r
1640     PM_BV = (int) BlackFalcon,\r
1641     PM_BSG = (int) BlackSilver,\r
1642     PM_BK = (int) BlackKing\r
1643 };\r
1644 \r
1645 static HFONT hPieceFont = NULL;\r
1646 static HBITMAP hPieceMask[(int) EmptySquare];\r
1647 static HBITMAP hPieceFace[(int) EmptySquare];\r
1648 static int fontBitmapSquareSize = 0;\r
1649 static char pieceToFontChar[(int) EmptySquare] =\r
1650                               { 'p', 'n', 'b', 'r', 'q', \r
1651                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1652                       'k', 'o', 'm', 'v', 't', 'w', \r
1653                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1654                                                               'l' };\r
1655 \r
1656 extern BOOL SetCharTable( char *table, const char * map );\r
1657 /* [HGM] moved to backend.c */\r
1658 \r
1659 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1660 {\r
1661     HBRUSH hbrush;\r
1662     BYTE r1 = GetRValue( color );\r
1663     BYTE g1 = GetGValue( color );\r
1664     BYTE b1 = GetBValue( color );\r
1665     BYTE r2 = r1 / 2;\r
1666     BYTE g2 = g1 / 2;\r
1667     BYTE b2 = b1 / 2;\r
1668     RECT rc;\r
1669 \r
1670     /* Create a uniform background first */\r
1671     hbrush = CreateSolidBrush( color );\r
1672     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1673     FillRect( hdc, &rc, hbrush );\r
1674     DeleteObject( hbrush );\r
1675     \r
1676     if( mode == 1 ) {\r
1677         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1678         int steps = squareSize / 2;\r
1679         int i;\r
1680 \r
1681         for( i=0; i<steps; i++ ) {\r
1682             BYTE r = r1 - (r1-r2) * i / steps;\r
1683             BYTE g = g1 - (g1-g2) * i / steps;\r
1684             BYTE b = b1 - (b1-b2) * i / steps;\r
1685 \r
1686             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1687             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1688             FillRect( hdc, &rc, hbrush );\r
1689             DeleteObject(hbrush);\r
1690         }\r
1691     }\r
1692     else if( mode == 2 ) {\r
1693         /* Diagonal gradient, good more or less for every piece */\r
1694         POINT triangle[3];\r
1695         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1696         HBRUSH hbrush_old;\r
1697         int steps = squareSize;\r
1698         int i;\r
1699 \r
1700         triangle[0].x = squareSize - steps;\r
1701         triangle[0].y = squareSize;\r
1702         triangle[1].x = squareSize;\r
1703         triangle[1].y = squareSize;\r
1704         triangle[2].x = squareSize;\r
1705         triangle[2].y = squareSize - steps;\r
1706 \r
1707         for( i=0; i<steps; i++ ) {\r
1708             BYTE r = r1 - (r1-r2) * i / steps;\r
1709             BYTE g = g1 - (g1-g2) * i / steps;\r
1710             BYTE b = b1 - (b1-b2) * i / steps;\r
1711 \r
1712             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1713             hbrush_old = SelectObject( hdc, hbrush );\r
1714             Polygon( hdc, triangle, 3 );\r
1715             SelectObject( hdc, hbrush_old );\r
1716             DeleteObject(hbrush);\r
1717             triangle[0].x++;\r
1718             triangle[2].y++;\r
1719         }\r
1720 \r
1721         SelectObject( hdc, hpen );\r
1722     }\r
1723 }\r
1724 \r
1725 /*\r
1726     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1727     seems to work ok. The main problem here is to find the "inside" of a chess\r
1728     piece: follow the steps as explained below.\r
1729 */\r
1730 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1731 {\r
1732     HBITMAP hbm;\r
1733     HBITMAP hbm_old;\r
1734     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1735     RECT rc;\r
1736     SIZE sz;\r
1737     POINT pt;\r
1738     int backColor = whitePieceColor; \r
1739     int foreColor = blackPieceColor;\r
1740     \r
1741     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1742         backColor = appData.fontBackColorWhite;\r
1743         foreColor = appData.fontForeColorWhite;\r
1744     }\r
1745     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1746         backColor = appData.fontBackColorBlack;\r
1747         foreColor = appData.fontForeColorBlack;\r
1748     }\r
1749 \r
1750     /* Mask */\r
1751     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1752 \r
1753     hbm_old = SelectObject( hdc, hbm );\r
1754 \r
1755     rc.left = 0;\r
1756     rc.top = 0;\r
1757     rc.right = squareSize;\r
1758     rc.bottom = squareSize;\r
1759 \r
1760     /* Step 1: background is now black */\r
1761     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1762 \r
1763     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1764 \r
1765     pt.x = (squareSize - sz.cx) / 2;\r
1766     pt.y = (squareSize - sz.cy) / 2;\r
1767 \r
1768     SetBkMode( hdc, TRANSPARENT );\r
1769     SetTextColor( hdc, chroma );\r
1770     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1771     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1772 \r
1773     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1774     /* Step 3: the area outside the piece is filled with white */\r
1775 //    FloodFill( hdc, 0, 0, chroma );\r
1776     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1777     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1778     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1779     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1780     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1781     /* \r
1782         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1783         but if the start point is not inside the piece we're lost!\r
1784         There should be a better way to do this... if we could create a region or path\r
1785         from the fill operation we would be fine for example.\r
1786     */\r
1787 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1788     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1789 \r
1790     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1791         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1792         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1793 \r
1794         SelectObject( dc2, bm2 );\r
1795         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1796         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1797         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1798         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1799         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1800 \r
1801         DeleteDC( dc2 );\r
1802         DeleteObject( bm2 );\r
1803     }\r
1804 \r
1805     SetTextColor( hdc, 0 );\r
1806     /* \r
1807         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1808         draw the piece again in black for safety.\r
1809     */\r
1810     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1811 \r
1812     SelectObject( hdc, hbm_old );\r
1813 \r
1814     if( hPieceMask[index] != NULL ) {\r
1815         DeleteObject( hPieceMask[index] );\r
1816     }\r
1817 \r
1818     hPieceMask[index] = hbm;\r
1819 \r
1820     /* Face */\r
1821     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1822 \r
1823     SelectObject( hdc, hbm );\r
1824 \r
1825     {\r
1826         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1827         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1828         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1829 \r
1830         SelectObject( dc1, hPieceMask[index] );\r
1831         SelectObject( dc2, bm2 );\r
1832         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1833         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1834         \r
1835         /* \r
1836             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1837             the piece background and deletes (makes transparent) the rest.\r
1838             Thanks to that mask, we are free to paint the background with the greates\r
1839             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1840             We use this, to make gradients and give the pieces a "roundish" look.\r
1841         */\r
1842         SetPieceBackground( hdc, backColor, 2 );\r
1843         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1844 \r
1845         DeleteDC( dc2 );\r
1846         DeleteDC( dc1 );\r
1847         DeleteObject( bm2 );\r
1848     }\r
1849 \r
1850     SetTextColor( hdc, foreColor );\r
1851     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1852 \r
1853     SelectObject( hdc, hbm_old );\r
1854 \r
1855     if( hPieceFace[index] != NULL ) {\r
1856         DeleteObject( hPieceFace[index] );\r
1857     }\r
1858 \r
1859     hPieceFace[index] = hbm;\r
1860 }\r
1861 \r
1862 static int TranslatePieceToFontPiece( int piece )\r
1863 {\r
1864     switch( piece ) {\r
1865     case BlackPawn:\r
1866         return PM_BP;\r
1867     case BlackKnight:\r
1868         return PM_BN;\r
1869     case BlackBishop:\r
1870         return PM_BB;\r
1871     case BlackRook:\r
1872         return PM_BR;\r
1873     case BlackQueen:\r
1874         return PM_BQ;\r
1875     case BlackKing:\r
1876         return PM_BK;\r
1877     case WhitePawn:\r
1878         return PM_WP;\r
1879     case WhiteKnight:\r
1880         return PM_WN;\r
1881     case WhiteBishop:\r
1882         return PM_WB;\r
1883     case WhiteRook:\r
1884         return PM_WR;\r
1885     case WhiteQueen:\r
1886         return PM_WQ;\r
1887     case WhiteKing:\r
1888         return PM_WK;\r
1889 \r
1890     case BlackAngel:\r
1891         return PM_BA;\r
1892     case BlackMarshall:\r
1893         return PM_BC;\r
1894     case BlackFerz:\r
1895         return PM_BF;\r
1896     case BlackNightrider:\r
1897         return PM_BH;\r
1898     case BlackAlfil:\r
1899         return PM_BE;\r
1900     case BlackWazir:\r
1901         return PM_BW;\r
1902     case BlackUnicorn:\r
1903         return PM_BU;\r
1904     case BlackCannon:\r
1905         return PM_BO;\r
1906     case BlackGrasshopper:\r
1907         return PM_BG;\r
1908     case BlackMan:\r
1909         return PM_BM;\r
1910     case BlackSilver:\r
1911         return PM_BSG;\r
1912     case BlackLance:\r
1913         return PM_BL;\r
1914     case BlackFalcon:\r
1915         return PM_BV;\r
1916     case BlackCobra:\r
1917         return PM_BS;\r
1918     case BlackCardinal:\r
1919         return PM_BAB;\r
1920     case BlackDragon:\r
1921         return PM_BD;\r
1922 \r
1923     case WhiteAngel:\r
1924         return PM_WA;\r
1925     case WhiteMarshall:\r
1926         return PM_WC;\r
1927     case WhiteFerz:\r
1928         return PM_WF;\r
1929     case WhiteNightrider:\r
1930         return PM_WH;\r
1931     case WhiteAlfil:\r
1932         return PM_WE;\r
1933     case WhiteWazir:\r
1934         return PM_WW;\r
1935     case WhiteUnicorn:\r
1936         return PM_WU;\r
1937     case WhiteCannon:\r
1938         return PM_WO;\r
1939     case WhiteGrasshopper:\r
1940         return PM_WG;\r
1941     case WhiteMan:\r
1942         return PM_WM;\r
1943     case WhiteSilver:\r
1944         return PM_WSG;\r
1945     case WhiteLance:\r
1946         return PM_WL;\r
1947     case WhiteFalcon:\r
1948         return PM_WV;\r
1949     case WhiteCobra:\r
1950         return PM_WS;\r
1951     case WhiteCardinal:\r
1952         return PM_WAB;\r
1953     case WhiteDragon:\r
1954         return PM_WD;\r
1955     }\r
1956 \r
1957     return 0;\r
1958 }\r
1959 \r
1960 void CreatePiecesFromFont()\r
1961 {\r
1962     LOGFONT lf;\r
1963     HDC hdc_window = NULL;\r
1964     HDC hdc = NULL;\r
1965     HFONT hfont_old;\r
1966     int fontHeight;\r
1967     int i;\r
1968 \r
1969     if( fontBitmapSquareSize < 0 ) {\r
1970         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
1971         return;\r
1972     }\r
1973 \r
1974     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
1975         fontBitmapSquareSize = -1;\r
1976         return;\r
1977     }\r
1978 \r
1979     if( fontBitmapSquareSize != squareSize ) {\r
1980         hdc_window = GetDC( hwndMain );\r
1981         hdc = CreateCompatibleDC( hdc_window );\r
1982 \r
1983         if( hPieceFont != NULL ) {\r
1984             DeleteObject( hPieceFont );\r
1985         }\r
1986         else {\r
1987             for( i=0; i<=(int)BlackKing; i++ ) {\r
1988                 hPieceMask[i] = NULL;\r
1989                 hPieceFace[i] = NULL;\r
1990             }\r
1991         }\r
1992 \r
1993         fontHeight = 75;\r
1994 \r
1995         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
1996             fontHeight = appData.fontPieceSize;\r
1997         }\r
1998 \r
1999         fontHeight = (fontHeight * squareSize) / 100;\r
2000 \r
2001         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2002         lf.lfWidth = 0;\r
2003         lf.lfEscapement = 0;\r
2004         lf.lfOrientation = 0;\r
2005         lf.lfWeight = FW_NORMAL;\r
2006         lf.lfItalic = 0;\r
2007         lf.lfUnderline = 0;\r
2008         lf.lfStrikeOut = 0;\r
2009         lf.lfCharSet = DEFAULT_CHARSET;\r
2010         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2011         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2012         lf.lfQuality = PROOF_QUALITY;\r
2013         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2014         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2015         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2016 \r
2017         hPieceFont = CreateFontIndirect( &lf );\r
2018 \r
2019         if( hPieceFont == NULL ) {\r
2020             fontBitmapSquareSize = -2;\r
2021         }\r
2022         else {\r
2023             /* Setup font-to-piece character table */\r
2024             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2025                 /* No (or wrong) global settings, try to detect the font */\r
2026                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2027                     /* Alpha */\r
2028                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2029                 }\r
2030                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2031                     /* DiagramTT* family */\r
2032                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2033                 }\r
2034                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2035                     /* Fairy symbols */\r
2036                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2037                 }\r
2038                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2039                     /* Good Companion (Some characters get warped as literal :-( */\r
2040                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2041                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2042                     SetCharTable(pieceToFontChar, s);\r
2043                 }\r
2044                 else {\r
2045                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2046                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2047                 }\r
2048             }\r
2049 \r
2050             /* Create bitmaps */\r
2051             hfont_old = SelectObject( hdc, hPieceFont );\r
2052             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2053                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2054                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2055 \r
2056             SelectObject( hdc, hfont_old );\r
2057 \r
2058             fontBitmapSquareSize = squareSize;\r
2059         }\r
2060     }\r
2061 \r
2062     if( hdc != NULL ) {\r
2063         DeleteDC( hdc );\r
2064     }\r
2065 \r
2066     if( hdc_window != NULL ) {\r
2067         ReleaseDC( hwndMain, hdc_window );\r
2068     }\r
2069 }\r
2070 \r
2071 HBITMAP\r
2072 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2073 {\r
2074   char name[128];\r
2075 \r
2076     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2077   if (gameInfo.event &&\r
2078       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2079       strcmp(name, "k80s") == 0) {\r
2080     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2081   }\r
2082   return LoadBitmap(hinst, name);\r
2083 }\r
2084 \r
2085 \r
2086 /* Insert a color into the program's logical palette\r
2087    structure.  This code assumes the given color is\r
2088    the result of the RGB or PALETTERGB macro, and it\r
2089    knows how those macros work (which is documented).\r
2090 */\r
2091 VOID\r
2092 InsertInPalette(COLORREF color)\r
2093 {\r
2094   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2095 \r
2096   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2097     DisplayFatalError(_("Too many colors"), 0, 1);\r
2098     pLogPal->palNumEntries--;\r
2099     return;\r
2100   }\r
2101 \r
2102   pe->peFlags = (char) 0;\r
2103   pe->peRed = (char) (0xFF & color);\r
2104   pe->peGreen = (char) (0xFF & (color >> 8));\r
2105   pe->peBlue = (char) (0xFF & (color >> 16));\r
2106   return;\r
2107 }\r
2108 \r
2109 \r
2110 VOID\r
2111 InitDrawingColors()\r
2112 {\r
2113   if (pLogPal == NULL) {\r
2114     /* Allocate enough memory for a logical palette with\r
2115      * PALETTESIZE entries and set the size and version fields\r
2116      * of the logical palette structure.\r
2117      */\r
2118     pLogPal = (NPLOGPALETTE)\r
2119       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2120                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2121     pLogPal->palVersion    = 0x300;\r
2122   }\r
2123   pLogPal->palNumEntries = 0;\r
2124 \r
2125   InsertInPalette(lightSquareColor);\r
2126   InsertInPalette(darkSquareColor);\r
2127   InsertInPalette(whitePieceColor);\r
2128   InsertInPalette(blackPieceColor);\r
2129   InsertInPalette(highlightSquareColor);\r
2130   InsertInPalette(premoveHighlightColor);\r
2131 \r
2132   /*  create a logical color palette according the information\r
2133    *  in the LOGPALETTE structure.\r
2134    */\r
2135   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2136 \r
2137   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2138   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2139   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2140   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2141   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2142   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2143   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2144   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2145   /* [AS] Force rendering of the font-based pieces */\r
2146   if( fontBitmapSquareSize > 0 ) {\r
2147     fontBitmapSquareSize = 0;\r
2148   }\r
2149 }\r
2150 \r
2151 \r
2152 int\r
2153 BoardWidth(int boardSize, int n)\r
2154 { /* [HGM] argument n added to allow different width and height */\r
2155   int lineGap = sizeInfo[boardSize].lineGap;\r
2156 \r
2157   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2158       lineGap = appData.overrideLineGap;\r
2159   }\r
2160 \r
2161   return (n + 1) * lineGap +\r
2162           n * sizeInfo[boardSize].squareSize;\r
2163 }\r
2164 \r
2165 /* Respond to board resize by dragging edge */\r
2166 VOID\r
2167 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2168 {\r
2169   BoardSize newSize = NUM_SIZES - 1;\r
2170   static int recurse = 0;\r
2171   if (IsIconic(hwndMain)) return;\r
2172   if (recurse > 0) return;\r
2173   recurse++;\r
2174   while (newSize > 0) {\r
2175         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2176         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2177            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2178     newSize--;\r
2179   } \r
2180   boardSize = newSize;\r
2181   InitDrawingSizes(boardSize, flags);\r
2182   recurse--;\r
2183 }\r
2184 \r
2185 \r
2186 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2187 \r
2188 VOID\r
2189 InitDrawingSizes(BoardSize boardSize, int flags)\r
2190 {\r
2191   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2192   ChessSquare piece;\r
2193   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2194   HDC hdc;\r
2195   SIZE clockSize, messageSize;\r
2196   HFONT oldFont;\r
2197   char buf[MSG_SIZ];\r
2198   char *str;\r
2199   HMENU hmenu = GetMenu(hwndMain);\r
2200   RECT crect, wrect, oldRect;\r
2201   int offby;\r
2202   LOGBRUSH logbrush;\r
2203 \r
2204   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2205   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2206 \r
2207   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2208   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2209 \r
2210   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2211   oldRect.top = wpMain.y;\r
2212   oldRect.right = wpMain.x + wpMain.width;\r
2213   oldRect.bottom = wpMain.y + wpMain.height;\r
2214 \r
2215   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2216   smallLayout = sizeInfo[boardSize].smallLayout;\r
2217   squareSize = sizeInfo[boardSize].squareSize;\r
2218   lineGap = sizeInfo[boardSize].lineGap;\r
2219   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2220 \r
2221   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2222       lineGap = appData.overrideLineGap;\r
2223   }\r
2224 \r
2225   if (tinyLayout != oldTinyLayout) {\r
2226     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
2227     if (tinyLayout) {\r
2228       style &= ~WS_SYSMENU;\r
2229       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2230                  "&Minimize\tCtrl+F4");\r
2231     } else {\r
2232       style |= WS_SYSMENU;\r
2233       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2234     }\r
2235     SetWindowLong(hwndMain, GWL_STYLE, style);\r
2236 \r
2237     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2238       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2239         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2240     }\r
2241     DrawMenuBar(hwndMain);\r
2242   }\r
2243 \r
2244   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2245   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2246 \r
2247   /* Get text area sizes */\r
2248   hdc = GetDC(hwndMain);\r
2249   if (appData.clockMode) {\r
2250     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2251   } else {\r
2252     snprintf(buf, MSG_SIZ, _("White"));\r
2253   }\r
2254   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2255   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2256   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2257   str = _("We only care about the height here");\r
2258   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2259   SelectObject(hdc, oldFont);\r
2260   ReleaseDC(hwndMain, hdc);\r
2261 \r
2262   /* Compute where everything goes */\r
2263   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2264         /* [HGM] logo: if either logo is on, reserve space for it */\r
2265         logoHeight =  2*clockSize.cy;\r
2266         leftLogoRect.left   = OUTER_MARGIN;\r
2267         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2268         leftLogoRect.top    = OUTER_MARGIN;\r
2269         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2270 \r
2271         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2272         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2273         rightLogoRect.top    = OUTER_MARGIN;\r
2274         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2275 \r
2276 \r
2277     whiteRect.left = leftLogoRect.right;\r
2278     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2279     whiteRect.top = OUTER_MARGIN;\r
2280     whiteRect.bottom = whiteRect.top + logoHeight;\r
2281 \r
2282     blackRect.right = rightLogoRect.left;\r
2283     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2284     blackRect.top = whiteRect.top;\r
2285     blackRect.bottom = whiteRect.bottom;\r
2286   } else {\r
2287     whiteRect.left = OUTER_MARGIN;\r
2288     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2289     whiteRect.top = OUTER_MARGIN;\r
2290     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2291 \r
2292     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2293     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2294     blackRect.top = whiteRect.top;\r
2295     blackRect.bottom = whiteRect.bottom;\r
2296 \r
2297     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2298   }\r
2299 \r
2300   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2301   if (appData.showButtonBar) {\r
2302     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2303       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2304   } else {\r
2305     messageRect.right = OUTER_MARGIN + boardWidth;\r
2306   }\r
2307   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2308   messageRect.bottom = messageRect.top + messageSize.cy;\r
2309 \r
2310   boardRect.left = OUTER_MARGIN;\r
2311   boardRect.right = boardRect.left + boardWidth;\r
2312   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2313   boardRect.bottom = boardRect.top + boardHeight;\r
2314 \r
2315   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2316   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2317   oldBoardSize = boardSize;\r
2318   oldTinyLayout = tinyLayout;\r
2319   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2320   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2321     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2322   winW *= 1 + twoBoards;\r
2323   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2324   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2325   wpMain.height = winH; //       without disturbing window attachments\r
2326   GetWindowRect(hwndMain, &wrect);\r
2327   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2328                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2329 \r
2330   // [HGM] placement: let attached windows follow size change.\r
2331   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2332   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2333   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2334   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2335   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2336 \r
2337   /* compensate if menu bar wrapped */\r
2338   GetClientRect(hwndMain, &crect);\r
2339   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2340   wpMain.height += offby;\r
2341   switch (flags) {\r
2342   case WMSZ_TOPLEFT:\r
2343     SetWindowPos(hwndMain, NULL, \r
2344                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2345                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2346     break;\r
2347 \r
2348   case WMSZ_TOPRIGHT:\r
2349   case WMSZ_TOP:\r
2350     SetWindowPos(hwndMain, NULL, \r
2351                  wrect.left, wrect.bottom - wpMain.height, \r
2352                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2353     break;\r
2354 \r
2355   case WMSZ_BOTTOMLEFT:\r
2356   case WMSZ_LEFT:\r
2357     SetWindowPos(hwndMain, NULL, \r
2358                  wrect.right - wpMain.width, wrect.top, \r
2359                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2360     break;\r
2361 \r
2362   case WMSZ_BOTTOMRIGHT:\r
2363   case WMSZ_BOTTOM:\r
2364   case WMSZ_RIGHT:\r
2365   default:\r
2366     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2367                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2368     break;\r
2369   }\r
2370 \r
2371   hwndPause = NULL;\r
2372   for (i = 0; i < N_BUTTONS; i++) {\r
2373     if (buttonDesc[i].hwnd != NULL) {\r
2374       DestroyWindow(buttonDesc[i].hwnd);\r
2375       buttonDesc[i].hwnd = NULL;\r
2376     }\r
2377     if (appData.showButtonBar) {\r
2378       buttonDesc[i].hwnd =\r
2379         CreateWindow("BUTTON", buttonDesc[i].label,\r
2380                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2381                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2382                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2383                      (HMENU) buttonDesc[i].id,\r
2384                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
2385       if (tinyLayout) {\r
2386         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2387                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2388                     MAKELPARAM(FALSE, 0));\r
2389       }\r
2390       if (buttonDesc[i].id == IDM_Pause)\r
2391         hwndPause = buttonDesc[i].hwnd;\r
2392       buttonDesc[i].wndproc = (WNDPROC)\r
2393         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
2394     }\r
2395   }\r
2396   if (gridPen != NULL) DeleteObject(gridPen);\r
2397   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2398   if (premovePen != NULL) DeleteObject(premovePen);\r
2399   if (lineGap != 0) {\r
2400     logbrush.lbStyle = BS_SOLID;\r
2401     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2402     gridPen =\r
2403       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2404                    lineGap, &logbrush, 0, NULL);\r
2405     logbrush.lbColor = highlightSquareColor;\r
2406     highlightPen =\r
2407       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2408                    lineGap, &logbrush, 0, NULL);\r
2409 \r
2410     logbrush.lbColor = premoveHighlightColor; \r
2411     premovePen =\r
2412       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2413                    lineGap, &logbrush, 0, NULL);\r
2414 \r
2415     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2416     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2417       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2418       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2419         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2420       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2421         BOARD_WIDTH * (squareSize + lineGap);\r
2422       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2423     }\r
2424     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2425       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2426       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2427         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2428         lineGap / 2 + (i * (squareSize + lineGap));\r
2429       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2430         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2431       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2432     }\r
2433   }\r
2434 \r
2435   /* [HGM] Licensing requirement */\r
2436 #ifdef GOTHIC\r
2437   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2438 #endif\r
2439 #ifdef FALCON\r
2440   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2441 #endif\r
2442   GothicPopUp( "", VariantNormal);\r
2443 \r
2444 \r
2445 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2446 \r
2447   /* Load piece bitmaps for this board size */\r
2448   for (i=0; i<=2; i++) {\r
2449     for (piece = WhitePawn;\r
2450          (int) piece < (int) BlackPawn;\r
2451          piece = (ChessSquare) ((int) piece + 1)) {\r
2452       if (pieceBitmap[i][piece] != NULL)\r
2453         DeleteObject(pieceBitmap[i][piece]);\r
2454     }\r
2455   }\r
2456 \r
2457   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2458   // Orthodox Chess pieces\r
2459   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2460   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2461   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2462   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2463   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2464   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2465   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2466   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2467   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2468   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2469   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2470   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2471   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2472   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2473   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2474   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2475     // in Shogi, Hijack the unused Queen for Lance\r
2476     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2477     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2478     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2479   } else {\r
2480     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2481     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2482     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2483   }\r
2484 \r
2485   if(squareSize <= 72 && squareSize >= 33) { \r
2486     /* A & C are available in most sizes now */\r
2487     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2488       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2489       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2490       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2491       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2492       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2493       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2494       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2495       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2496       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2497       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2498       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2499       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2500     } else { // Smirf-like\r
2501       if(gameInfo.variant == VariantSChess) {\r
2502         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2503         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2504         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2505       } else {\r
2506         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2507         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2508         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2509       }\r
2510     }\r
2511     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2512       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2513       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2514       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2515     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2516       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2517       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2518       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2519     } else { // WinBoard standard\r
2520       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2521       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2522       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2523     }\r
2524   }\r
2525 \r
2526 \r
2527   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2528     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2529     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2530     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2531     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2532     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2533     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2534     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2535     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2536     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2537     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2538     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2539     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2540     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2541     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2542     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2543     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2544     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2545     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2546     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2547     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2548     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2549     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2550     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2551     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2552     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2553     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2554     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2555     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2556     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2557     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2558 \r
2559     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2560       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2561       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2562       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2563       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2564       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2565       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2566       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2567       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2568       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2569       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2570       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2571       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2572     } else {\r
2573       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2574       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2575       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2576       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2577       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2578       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2579       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2580       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2581       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2582       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2583       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2584       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2585     }\r
2586 \r
2587   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2588     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2589     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2590     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2591     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2592     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2593     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2594     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2595     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2596     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2597     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2598     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2599     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2600     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2601     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2602   }\r
2603 \r
2604 \r
2605   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2606   /* special Shogi support in this size */\r
2607   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2608       for (piece = WhitePawn;\r
2609            (int) piece < (int) BlackPawn;\r
2610            piece = (ChessSquare) ((int) piece + 1)) {\r
2611         if (pieceBitmap[i][piece] != NULL)\r
2612           DeleteObject(pieceBitmap[i][piece]);\r
2613       }\r
2614     }\r
2615   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2616   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2617   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2618   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2619   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2620   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2621   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2622   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2623   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2624   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2625   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2626   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2627   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2628   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2629   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2630   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2631   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2632   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2633   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2634   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2635   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2636   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2637   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2638   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2639   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2640   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2641   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2642   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2643   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2644   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2645   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2646   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2647   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2648   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2649   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2650   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2651   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2652   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2653   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2654   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2655   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2656   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2657   minorSize = 0;\r
2658   }\r
2659 }\r
2660 \r
2661 HBITMAP\r
2662 PieceBitmap(ChessSquare p, int kind)\r
2663 {\r
2664   if ((int) p >= (int) BlackPawn)\r
2665     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2666 \r
2667   return pieceBitmap[kind][(int) p];\r
2668 }\r
2669 \r
2670 /***************************************************************/\r
2671 \r
2672 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2673 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2674 /*\r
2675 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2676 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2677 */\r
2678 \r
2679 VOID\r
2680 SquareToPos(int row, int column, int * x, int * y)\r
2681 {\r
2682   if (flipView) {\r
2683     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2684     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2685   } else {\r
2686     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2687     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2688   }\r
2689 }\r
2690 \r
2691 VOID\r
2692 DrawCoordsOnDC(HDC hdc)\r
2693 {\r
2694   static char files[24] = {'0', '1','2','3','4','5','6','7','8','9','0','1','1','0','9','8','7','6','5','4','3','2','1','0'};\r
2695   static char ranks[24] = {'l', 'k','j','i','h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h','i','j','k','l'};\r
2696   char str[2] = { NULLCHAR, NULLCHAR };\r
2697   int oldMode, oldAlign, x, y, start, i;\r
2698   HFONT oldFont;\r
2699   HBRUSH oldBrush;\r
2700 \r
2701   if (!appData.showCoords)\r
2702     return;\r
2703 \r
2704   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
2705 \r
2706   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2707   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2708   oldAlign = GetTextAlign(hdc);\r
2709   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2710 \r
2711   y = boardRect.top + lineGap;\r
2712   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2713 \r
2714   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2715   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2716     str[0] = files[start + i];\r
2717     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2718     y += squareSize + lineGap;\r
2719   }\r
2720 \r
2721   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
2722 \r
2723   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2724   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2725     str[0] = ranks[start + i];\r
2726     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2727     x += squareSize + lineGap;\r
2728   }    \r
2729 \r
2730   SelectObject(hdc, oldBrush);\r
2731   SetBkMode(hdc, oldMode);\r
2732   SetTextAlign(hdc, oldAlign);\r
2733   SelectObject(hdc, oldFont);\r
2734 }\r
2735 \r
2736 VOID\r
2737 DrawGridOnDC(HDC hdc)\r
2738 {\r
2739   HPEN oldPen;\r
2740  \r
2741   if (lineGap != 0) {\r
2742     oldPen = SelectObject(hdc, gridPen);\r
2743     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2744     SelectObject(hdc, oldPen);\r
2745   }\r
2746 }\r
2747 \r
2748 #define HIGHLIGHT_PEN 0\r
2749 #define PREMOVE_PEN   1\r
2750 \r
2751 VOID\r
2752 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2753 {\r
2754   int x1, y1;\r
2755   HPEN oldPen, hPen;\r
2756   if (lineGap == 0) return;\r
2757   if (flipView) {\r
2758     x1 = boardRect.left +\r
2759       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2760     y1 = boardRect.top +\r
2761       lineGap/2 + y * (squareSize + lineGap);\r
2762   } else {\r
2763     x1 = boardRect.left +\r
2764       lineGap/2 + x * (squareSize + lineGap);\r
2765     y1 = boardRect.top +\r
2766       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2767   }\r
2768   hPen = pen ? premovePen : highlightPen;\r
2769   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2770   MoveToEx(hdc, x1, y1, NULL);\r
2771   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2772   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2773   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2774   LineTo(hdc, x1, y1);\r
2775   SelectObject(hdc, oldPen);\r
2776 }\r
2777 \r
2778 VOID\r
2779 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2780 {\r
2781   int i;\r
2782   for (i=0; i<2; i++) {\r
2783     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2784       DrawHighlightOnDC(hdc, TRUE,\r
2785                         h->sq[i].x, h->sq[i].y,\r
2786                         pen);\r
2787   }\r
2788 }\r
2789 \r
2790 /* Note: sqcolor is used only in monoMode */\r
2791 /* Note that this code is largely duplicated in woptions.c,\r
2792    function DrawSampleSquare, so that needs to be updated too */\r
2793 VOID\r
2794 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2795 {\r
2796   HBITMAP oldBitmap;\r
2797   HBRUSH oldBrush;\r
2798   int tmpSize;\r
2799 \r
2800   if (appData.blindfold) return;\r
2801 \r
2802   /* [AS] Use font-based pieces if needed */\r
2803   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2804     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2805     CreatePiecesFromFont();\r
2806 \r
2807     if( fontBitmapSquareSize == squareSize ) {\r
2808         int index = TranslatePieceToFontPiece(piece);\r
2809 \r
2810         SelectObject( tmphdc, hPieceMask[ index ] );\r
2811 \r
2812       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2813         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2814       else\r
2815         BitBlt( hdc,\r
2816             x, y,\r
2817             squareSize, squareSize,\r
2818             tmphdc,\r
2819             0, 0,\r
2820             SRCAND );\r
2821 \r
2822         SelectObject( tmphdc, hPieceFace[ index ] );\r
2823 \r
2824       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2825         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2826       else\r
2827         BitBlt( hdc,\r
2828             x, y,\r
2829             squareSize, squareSize,\r
2830             tmphdc,\r
2831             0, 0,\r
2832             SRCPAINT );\r
2833 \r
2834         return;\r
2835     }\r
2836   }\r
2837 \r
2838   if (appData.monoMode) {\r
2839     SelectObject(tmphdc, PieceBitmap(piece, \r
2840       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2841     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2842            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2843   } else {\r
2844     tmpSize = squareSize;\r
2845     if(minorSize &&\r
2846         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2847          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2848       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2849       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2850       x += (squareSize - minorSize)>>1;\r
2851       y += squareSize - minorSize - 2;\r
2852       tmpSize = minorSize;\r
2853     }\r
2854     if (color || appData.allWhite ) {\r
2855       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2856       if( color )\r
2857               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2858       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2859       if(appData.upsideDown && color==flipView)\r
2860         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2861       else\r
2862         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2863       /* Use black for outline of white pieces */\r
2864       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2865       if(appData.upsideDown && color==flipView)\r
2866         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2867       else\r
2868         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2869     } else {\r
2870       /* Use square color for details of black pieces */\r
2871       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2872       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2873       if(appData.upsideDown && !flipView)\r
2874         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2875       else\r
2876         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2877     }\r
2878     SelectObject(hdc, oldBrush);\r
2879     SelectObject(tmphdc, oldBitmap);\r
2880   }\r
2881 }\r
2882 \r
2883 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2884 int GetBackTextureMode( int algo )\r
2885 {\r
2886     int result = BACK_TEXTURE_MODE_DISABLED;\r
2887 \r
2888     switch( algo ) \r
2889     {\r
2890         case BACK_TEXTURE_MODE_PLAIN:\r
2891             result = 1; /* Always use identity map */\r
2892             break;\r
2893         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2894             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2895             break;\r
2896     }\r
2897 \r
2898     return result;\r
2899 }\r
2900 \r
2901 /* \r
2902     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2903     to handle redraws cleanly (as random numbers would always be different).\r
2904 */\r
2905 VOID RebuildTextureSquareInfo()\r
2906 {\r
2907     BITMAP bi;\r
2908     int lite_w = 0;\r
2909     int lite_h = 0;\r
2910     int dark_w = 0;\r
2911     int dark_h = 0;\r
2912     int row;\r
2913     int col;\r
2914 \r
2915     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2916 \r
2917     if( liteBackTexture != NULL ) {\r
2918         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2919             lite_w = bi.bmWidth;\r
2920             lite_h = bi.bmHeight;\r
2921         }\r
2922     }\r
2923 \r
2924     if( darkBackTexture != NULL ) {\r
2925         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2926             dark_w = bi.bmWidth;\r
2927             dark_h = bi.bmHeight;\r
2928         }\r
2929     }\r
2930 \r
2931     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2932         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2933             if( (col + row) & 1 ) {\r
2934                 /* Lite square */\r
2935                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2936                   if( lite_w >= squareSize*BOARD_WIDTH )\r
2937                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
2938                   else\r
2939                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2940                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
2941                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
2942                   else\r
2943                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2944                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2945                 }\r
2946             }\r
2947             else {\r
2948                 /* Dark square */\r
2949                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
2950                   if( dark_w >= squareSize*BOARD_WIDTH )\r
2951                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
2952                   else\r
2953                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
2954                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
2955                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
2956                   else\r
2957                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
2958                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
2959                 }\r
2960             }\r
2961         }\r
2962     }\r
2963 }\r
2964 \r
2965 /* [AS] Arrow highlighting support */\r
2966 \r
2967 static double A_WIDTH = 5; /* Width of arrow body */\r
2968 \r
2969 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
2970 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
2971 \r
2972 static double Sqr( double x )\r
2973 {\r
2974     return x*x;\r
2975 }\r
2976 \r
2977 static int Round( double x )\r
2978 {\r
2979     return (int) (x + 0.5);\r
2980 }\r
2981 \r
2982 /* Draw an arrow between two points using current settings */\r
2983 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
2984 {\r
2985     POINT arrow[7];\r
2986     double dx, dy, j, k, x, y;\r
2987 \r
2988     if( d_x == s_x ) {\r
2989         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2990 \r
2991         arrow[0].x = s_x + A_WIDTH + 0.5;\r
2992         arrow[0].y = s_y;\r
2993 \r
2994         arrow[1].x = s_x + A_WIDTH + 0.5;\r
2995         arrow[1].y = d_y - h;\r
2996 \r
2997         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
2998         arrow[2].y = d_y - h;\r
2999 \r
3000         arrow[3].x = d_x;\r
3001         arrow[3].y = d_y;\r
3002 \r
3003         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3004         arrow[5].y = d_y - h;\r
3005 \r
3006         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3007         arrow[4].y = d_y - h;\r
3008 \r
3009         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3010         arrow[6].y = s_y;\r
3011     }\r
3012     else if( d_y == s_y ) {\r
3013         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3014 \r
3015         arrow[0].x = s_x;\r
3016         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3017 \r
3018         arrow[1].x = d_x - w;\r
3019         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3020 \r
3021         arrow[2].x = d_x - w;\r
3022         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3023 \r
3024         arrow[3].x = d_x;\r
3025         arrow[3].y = d_y;\r
3026 \r
3027         arrow[5].x = d_x - w;\r
3028         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3029 \r
3030         arrow[4].x = d_x - w;\r
3031         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3032 \r
3033         arrow[6].x = s_x;\r
3034         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3035     }\r
3036     else {\r
3037         /* [AS] Needed a lot of paper for this! :-) */\r
3038         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3039         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3040   \r
3041         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3042 \r
3043         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3044 \r
3045         x = s_x;\r
3046         y = s_y;\r
3047 \r
3048         arrow[0].x = Round(x - j);\r
3049         arrow[0].y = Round(y + j*dx);\r
3050 \r
3051         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3052         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3053 \r
3054         if( d_x > s_x ) {\r
3055             x = (double) d_x - k;\r
3056             y = (double) d_y - k*dy;\r
3057         }\r
3058         else {\r
3059             x = (double) d_x + k;\r
3060             y = (double) d_y + k*dy;\r
3061         }\r
3062 \r
3063         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3064 \r
3065         arrow[6].x = Round(x - j);\r
3066         arrow[6].y = Round(y + j*dx);\r
3067 \r
3068         arrow[2].x = Round(arrow[6].x + 2*j);\r
3069         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3070 \r
3071         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3072         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3073 \r
3074         arrow[4].x = d_x;\r
3075         arrow[4].y = d_y;\r
3076 \r
3077         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3078         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3079     }\r
3080 \r
3081     Polygon( hdc, arrow, 7 );\r
3082 }\r
3083 \r
3084 /* [AS] Draw an arrow between two squares */\r
3085 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3086 {\r
3087     int s_x, s_y, d_x, d_y;\r
3088     HPEN hpen;\r
3089     HPEN holdpen;\r
3090     HBRUSH hbrush;\r
3091     HBRUSH holdbrush;\r
3092     LOGBRUSH stLB;\r
3093 \r
3094     if( s_col == d_col && s_row == d_row ) {\r
3095         return;\r
3096     }\r
3097 \r
3098     /* Get source and destination points */\r
3099     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3100     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3101 \r
3102     if( d_y > s_y ) {\r
3103         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3104     }\r
3105     else if( d_y < s_y ) {\r
3106         d_y += squareSize / 2 + squareSize / 4;\r
3107     }\r
3108     else {\r
3109         d_y += squareSize / 2;\r
3110     }\r
3111 \r
3112     if( d_x > s_x ) {\r
3113         d_x += squareSize / 2 - squareSize / 4;\r
3114     }\r
3115     else if( d_x < s_x ) {\r
3116         d_x += squareSize / 2 + squareSize / 4;\r
3117     }\r
3118     else {\r
3119         d_x += squareSize / 2;\r
3120     }\r
3121 \r
3122     s_x += squareSize / 2;\r
3123     s_y += squareSize / 2;\r
3124 \r
3125     /* Adjust width */\r
3126     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3127 \r
3128     /* Draw */\r
3129     stLB.lbStyle = BS_SOLID;\r
3130     stLB.lbColor = appData.highlightArrowColor;\r
3131     stLB.lbHatch = 0;\r
3132 \r
3133     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3134     holdpen = SelectObject( hdc, hpen );\r
3135     hbrush = CreateBrushIndirect( &stLB );\r
3136     holdbrush = SelectObject( hdc, hbrush );\r
3137 \r
3138     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3139 \r
3140     SelectObject( hdc, holdpen );\r
3141     SelectObject( hdc, holdbrush );\r
3142     DeleteObject( hpen );\r
3143     DeleteObject( hbrush );\r
3144 }\r
3145 \r
3146 BOOL HasHighlightInfo()\r
3147 {\r
3148     BOOL result = FALSE;\r
3149 \r
3150     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3151         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3152     {\r
3153         result = TRUE;\r
3154     }\r
3155 \r
3156     return result;\r
3157 }\r
3158 \r
3159 BOOL IsDrawArrowEnabled()\r
3160 {\r
3161     BOOL result = FALSE;\r
3162 \r
3163     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3164         result = TRUE;\r
3165     }\r
3166 \r
3167     return result;\r
3168 }\r
3169 \r
3170 VOID DrawArrowHighlight( HDC hdc )\r
3171 {\r
3172     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3173         DrawArrowBetweenSquares( hdc,\r
3174             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3175             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3176     }\r
3177 }\r
3178 \r
3179 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3180 {\r
3181     HRGN result = NULL;\r
3182 \r
3183     if( HasHighlightInfo() ) {\r
3184         int x1, y1, x2, y2;\r
3185         int sx, sy, dx, dy;\r
3186 \r
3187         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3188         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3189 \r
3190         sx = MIN( x1, x2 );\r
3191         sy = MIN( y1, y2 );\r
3192         dx = MAX( x1, x2 ) + squareSize;\r
3193         dy = MAX( y1, y2 ) + squareSize;\r
3194 \r
3195         result = CreateRectRgn( sx, sy, dx, dy );\r
3196     }\r
3197 \r
3198     return result;\r
3199 }\r
3200 \r
3201 /*\r
3202     Warning: this function modifies the behavior of several other functions. \r
3203     \r
3204     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3205     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3206     repaint is scattered all over the place, which is not good for features such as\r
3207     "arrow highlighting" that require a full repaint of the board.\r
3208 \r
3209     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3210     user interaction, when speed is not so important) but especially to avoid errors\r
3211     in the displayed graphics.\r
3212 \r
3213     In such patched places, I always try refer to this function so there is a single\r
3214     place to maintain knowledge.\r
3215     \r
3216     To restore the original behavior, just return FALSE unconditionally.\r
3217 */\r
3218 BOOL IsFullRepaintPreferrable()\r
3219 {\r
3220     BOOL result = FALSE;\r
3221 \r
3222     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3223         /* Arrow may appear on the board */\r
3224         result = TRUE;\r
3225     }\r
3226 \r
3227     return result;\r
3228 }\r
3229 \r
3230 /* \r
3231     This function is called by DrawPosition to know whether a full repaint must\r
3232     be forced or not.\r
3233 \r
3234     Only DrawPosition may directly call this function, which makes use of \r
3235     some state information. Other function should call DrawPosition specifying \r
3236     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3237 */\r
3238 BOOL DrawPositionNeedsFullRepaint()\r
3239 {\r
3240     BOOL result = FALSE;\r
3241 \r
3242     /* \r
3243         Probably a slightly better policy would be to trigger a full repaint\r
3244         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3245         but animation is fast enough that it's difficult to notice.\r
3246     */\r
3247     if( animInfo.piece == EmptySquare ) {\r
3248         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3249             result = TRUE;\r
3250         }\r
3251     }\r
3252 \r
3253     return result;\r
3254 }\r
3255 \r
3256 VOID\r
3257 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3258 {\r
3259   int row, column, x, y, square_color, piece_color;\r
3260   ChessSquare piece;\r
3261   HBRUSH oldBrush;\r
3262   HDC texture_hdc = NULL;\r
3263 \r
3264   /* [AS] Initialize background textures if needed */\r
3265   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3266       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3267       if( backTextureSquareSize != squareSize \r
3268        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3269           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3270           backTextureSquareSize = squareSize;\r
3271           RebuildTextureSquareInfo();\r
3272       }\r
3273 \r
3274       texture_hdc = CreateCompatibleDC( hdc );\r
3275   }\r
3276 \r
3277   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3278     for (column = 0; column < BOARD_WIDTH; column++) {\r
3279   \r
3280       SquareToPos(row, column, &x, &y);\r
3281 \r
3282       piece = board[row][column];\r
3283 \r
3284       square_color = ((column + row) % 2) == 1;\r
3285       if( gameInfo.variant == VariantXiangqi ) {\r
3286           square_color = !InPalace(row, column);\r
3287           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3288           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3289       }\r
3290       piece_color = (int) piece < (int) BlackPawn;\r
3291 \r
3292 \r
3293       /* [HGM] holdings file: light square or black */\r
3294       if(column == BOARD_LEFT-2) {\r
3295             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3296                 square_color = 1;\r
3297             else {\r
3298                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3299                 continue;\r
3300             }\r
3301       } else\r
3302       if(column == BOARD_RGHT + 1 ) {\r
3303             if( row < gameInfo.holdingsSize )\r
3304                 square_color = 1;\r
3305             else {\r
3306                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3307                 continue;\r
3308             }\r
3309       }\r
3310       if(column == BOARD_LEFT-1 ) /* left align */\r
3311             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3312       else if( column == BOARD_RGHT) /* right align */\r
3313             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3314       else\r
3315       if (appData.monoMode) {\r
3316         if (piece == EmptySquare) {\r
3317           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3318                  square_color ? WHITENESS : BLACKNESS);\r
3319         } else {\r
3320           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3321         }\r
3322       } \r
3323       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
3324           /* [AS] Draw the square using a texture bitmap */\r
3325           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3326           int r = row, c = column; // [HGM] do not flip board in flipView\r
3327           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3328 \r
3329           DrawTile( x, y, \r
3330               squareSize, squareSize, \r
3331               hdc, \r
3332               texture_hdc,\r
3333               backTextureSquareInfo[r][c].mode,\r
3334               backTextureSquareInfo[r][c].x,\r
3335               backTextureSquareInfo[r][c].y );\r
3336 \r
3337           SelectObject( texture_hdc, hbm );\r
3338 \r
3339           if (piece != EmptySquare) {\r
3340               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3341           }\r
3342       }\r
3343       else {\r
3344         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3345 \r
3346         oldBrush = SelectObject(hdc, brush );\r
3347         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3348         SelectObject(hdc, oldBrush);\r
3349         if (piece != EmptySquare)\r
3350           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3351       }\r
3352     }\r
3353   }\r
3354 \r
3355   if( texture_hdc != NULL ) {\r
3356     DeleteDC( texture_hdc );\r
3357   }\r
3358 }\r
3359 \r
3360 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3361 void fputDW(FILE *f, int x)\r
3362 {\r
3363         fputc(x     & 255, f);\r
3364         fputc(x>>8  & 255, f);\r
3365         fputc(x>>16 & 255, f);\r
3366         fputc(x>>24 & 255, f);\r
3367 }\r
3368 \r
3369 #define MAX_CLIPS 200   /* more than enough */\r
3370 \r
3371 VOID\r
3372 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3373 {\r
3374 //  HBITMAP bufferBitmap;\r
3375   BITMAP bi;\r
3376 //  RECT Rect;\r
3377   HDC tmphdc;\r
3378   HBITMAP hbm;\r
3379   int w = 100, h = 50;\r
3380 \r
3381   if(logo == NULL) return;\r
3382 //  GetClientRect(hwndMain, &Rect);\r
3383 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3384 //                                      Rect.bottom-Rect.top+1);\r
3385   tmphdc = CreateCompatibleDC(hdc);\r
3386   hbm = SelectObject(tmphdc, logo);\r
3387   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3388             w = bi.bmWidth;\r
3389             h = bi.bmHeight;\r
3390   }\r
3391   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3392                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3393   SelectObject(tmphdc, hbm);\r
3394   DeleteDC(tmphdc);\r
3395 }\r
3396 \r
3397 static HDC hdcSeek;\r
3398 \r
3399 // [HGM] seekgraph\r
3400 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3401 {\r
3402     POINT stPt;\r
3403     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3404     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3405     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3406     SelectObject( hdcSeek, hp );\r
3407 }\r
3408 \r
3409 // front-end wrapper for drawing functions to do rectangles\r
3410 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3411 {\r
3412     HPEN hp;\r
3413     RECT rc;\r
3414 \r
3415     if (hdcSeek == NULL) {\r
3416     hdcSeek = GetDC(hwndMain);\r
3417       if (!appData.monoMode) {\r
3418         SelectPalette(hdcSeek, hPal, FALSE);\r
3419         RealizePalette(hdcSeek);\r
3420       }\r
3421     }\r
3422     hp = SelectObject( hdcSeek, gridPen );\r
3423     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3424     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3425     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3426     SelectObject( hdcSeek, hp );\r
3427 }\r
3428 \r
3429 // front-end wrapper for putting text in graph\r
3430 void DrawSeekText(char *buf, int x, int y)\r
3431 {\r
3432         SIZE stSize;\r
3433         SetBkMode( hdcSeek, TRANSPARENT );\r
3434         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3435         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3436 }\r
3437 \r
3438 void DrawSeekDot(int x, int y, int color)\r
3439 {\r
3440         int square = color & 0x80;\r
3441         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3442                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3443         color &= 0x7F;\r
3444         if(square)\r
3445             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3446                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3447         else\r
3448             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3449                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3450             SelectObject(hdcSeek, oldBrush);\r
3451 }\r
3452 \r
3453 VOID\r
3454 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3455 {\r
3456   static Board lastReq[2], lastDrawn[2];\r
3457   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3458   static int lastDrawnFlipView = 0;\r
3459   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3460   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3461   HDC tmphdc;\r
3462   HDC hdcmem;\r
3463   HBITMAP bufferBitmap;\r
3464   HBITMAP oldBitmap;\r
3465   RECT Rect;\r
3466   HRGN clips[MAX_CLIPS];\r
3467   ChessSquare dragged_piece = EmptySquare;\r
3468   int nr = twoBoards*partnerUp;\r
3469 \r
3470   /* I'm undecided on this - this function figures out whether a full\r
3471    * repaint is necessary on its own, so there's no real reason to have the\r
3472    * caller tell it that.  I think this can safely be set to FALSE - but\r
3473    * if we trust the callers not to request full repaints unnessesarily, then\r
3474    * we could skip some clipping work.  In other words, only request a full\r
3475    * redraw when the majority of pieces have changed positions (ie. flip, \r
3476    * gamestart and similar)  --Hawk\r
3477    */\r
3478   Boolean fullrepaint = repaint;\r
3479 \r
3480   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3481 \r
3482   if( DrawPositionNeedsFullRepaint() ) {\r
3483       fullrepaint = TRUE;\r
3484   }\r
3485 \r
3486   if (board == NULL) {\r
3487     if (!lastReqValid[nr]) {\r
3488       return;\r
3489     }\r
3490     board = lastReq[nr];\r
3491   } else {\r
3492     CopyBoard(lastReq[nr], board);\r
3493     lastReqValid[nr] = 1;\r
3494   }\r
3495 \r
3496   if (doingSizing) {\r
3497     return;\r
3498   }\r
3499 \r
3500   if (IsIconic(hwndMain)) {\r
3501     return;\r
3502   }\r
3503 \r
3504   if (hdc == NULL) {\r
3505     hdc = GetDC(hwndMain);\r
3506     if (!appData.monoMode) {\r
3507       SelectPalette(hdc, hPal, FALSE);\r
3508       RealizePalette(hdc);\r
3509     }\r
3510     releaseDC = TRUE;\r
3511   } else {\r
3512     releaseDC = FALSE;\r
3513   }\r
3514 \r
3515   /* Create some work-DCs */\r
3516   hdcmem = CreateCompatibleDC(hdc);\r
3517   tmphdc = CreateCompatibleDC(hdc);\r
3518 \r
3519   /* If dragging is in progress, we temporarely remove the piece */\r
3520   /* [HGM] or temporarily decrease count if stacked              */\r
3521   /*       !! Moved to before board compare !!                   */\r
3522   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3523     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3524     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3525             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3526         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3527     } else \r
3528     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3529             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3530         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3531     } else \r
3532         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3533   }\r
3534 \r
3535   /* Figure out which squares need updating by comparing the \r
3536    * newest board with the last drawn board and checking if\r
3537    * flipping has changed.\r
3538    */\r
3539   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3540     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3541       for (column = 0; column < BOARD_WIDTH; column++) {\r
3542         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3543           SquareToPos(row, column, &x, &y);\r
3544           clips[num_clips++] =\r
3545             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3546         }\r
3547       }\r
3548     }\r
3549    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3550     for (i=0; i<2; i++) {\r
3551       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3552           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3553         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3554             lastDrawnHighlight.sq[i].y >= 0) {\r
3555           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3556                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3557           clips[num_clips++] =\r
3558             CreateRectRgn(x - lineGap, y - lineGap, \r
3559                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3560         }\r
3561         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3562           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3563           clips[num_clips++] =\r
3564             CreateRectRgn(x - lineGap, y - lineGap, \r
3565                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3566         }\r
3567       }\r
3568     }\r
3569     for (i=0; i<2; i++) {\r
3570       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3571           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3572         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3573             lastDrawnPremove.sq[i].y >= 0) {\r
3574           SquareToPos(lastDrawnPremove.sq[i].y,\r
3575                       lastDrawnPremove.sq[i].x, &x, &y);\r
3576           clips[num_clips++] =\r
3577             CreateRectRgn(x - lineGap, y - lineGap, \r
3578                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3579         }\r
3580         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3581             premoveHighlightInfo.sq[i].y >= 0) {\r
3582           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3583                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3584           clips[num_clips++] =\r
3585             CreateRectRgn(x - lineGap, y - lineGap, \r
3586                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3587         }\r
3588       }\r
3589     }\r
3590    } else { // nr == 1\r
3591         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3592         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3593         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3594         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3595       for (i=0; i<2; i++) {\r
3596         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3597             partnerHighlightInfo.sq[i].y >= 0) {\r
3598           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3599                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3600           clips[num_clips++] =\r
3601             CreateRectRgn(x - lineGap, y - lineGap, \r
3602                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3603         }\r
3604         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3605             oldPartnerHighlight.sq[i].y >= 0) {\r
3606           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3607                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3608           clips[num_clips++] =\r
3609             CreateRectRgn(x - lineGap, y - lineGap, \r
3610                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3611         }\r
3612       }\r
3613    }\r
3614   } else {\r
3615     fullrepaint = TRUE;\r
3616   }\r
3617 \r
3618   /* Create a buffer bitmap - this is the actual bitmap\r
3619    * being written to.  When all the work is done, we can\r
3620    * copy it to the real DC (the screen).  This avoids\r
3621    * the problems with flickering.\r
3622    */\r
3623   GetClientRect(hwndMain, &Rect);\r
3624   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3625                                         Rect.bottom-Rect.top+1);\r
3626   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3627   if (!appData.monoMode) {\r
3628     SelectPalette(hdcmem, hPal, FALSE);\r
3629   }\r
3630 \r
3631   /* Create clips for dragging */\r
3632   if (!fullrepaint) {\r
3633     if (dragInfo.from.x >= 0) {\r
3634       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3635       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3636     }\r
3637     if (dragInfo.start.x >= 0) {\r
3638       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3639       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3640     }\r
3641     if (dragInfo.pos.x >= 0) {\r
3642       x = dragInfo.pos.x - squareSize / 2;\r
3643       y = dragInfo.pos.y - squareSize / 2;\r
3644       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3645     }\r
3646     if (dragInfo.lastpos.x >= 0) {\r
3647       x = dragInfo.lastpos.x - squareSize / 2;\r
3648       y = dragInfo.lastpos.y - squareSize / 2;\r
3649       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3650     }\r
3651   }\r
3652 \r
3653   /* Are we animating a move?  \r
3654    * If so, \r
3655    *   - remove the piece from the board (temporarely)\r
3656    *   - calculate the clipping region\r
3657    */\r
3658   if (!fullrepaint) {\r
3659     if (animInfo.piece != EmptySquare) {\r
3660       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3661       x = boardRect.left + animInfo.lastpos.x;\r
3662       y = boardRect.top + animInfo.lastpos.y;\r
3663       x2 = boardRect.left + animInfo.pos.x;\r
3664       y2 = boardRect.top + animInfo.pos.y;\r
3665       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3666       /* Slight kludge.  The real problem is that after AnimateMove is\r
3667          done, the position on the screen does not match lastDrawn.\r
3668          This currently causes trouble only on e.p. captures in\r
3669          atomic, where the piece moves to an empty square and then\r
3670          explodes.  The old and new positions both had an empty square\r
3671          at the destination, but animation has drawn a piece there and\r
3672          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3673       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3674     }\r
3675   }\r
3676 \r
3677   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3678   if (num_clips == 0)\r
3679     fullrepaint = TRUE;\r
3680 \r
3681   /* Set clipping on the memory DC */\r
3682   if (!fullrepaint) {\r
3683     SelectClipRgn(hdcmem, clips[0]);\r
3684     for (x = 1; x < num_clips; x++) {\r
3685       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3686         abort();  // this should never ever happen!\r
3687     }\r
3688   }\r
3689 \r
3690   /* Do all the drawing to the memory DC */\r
3691   if(explodeInfo.radius) { // [HGM] atomic\r
3692         HBRUSH oldBrush;\r
3693         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3694         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3695         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3696         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3697         x += squareSize/2;\r
3698         y += squareSize/2;\r
3699         if(!fullrepaint) {\r
3700           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3701           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3702         }\r
3703         DrawGridOnDC(hdcmem);\r
3704         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3705         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3706         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3707         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3708         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3709         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3710         SelectObject(hdcmem, oldBrush);\r
3711   } else {\r
3712     DrawGridOnDC(hdcmem);\r
3713     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3714         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3715         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3716     } else {\r
3717         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3718         oldPartnerHighlight = partnerHighlightInfo;\r
3719     }\r
3720     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3721   }\r
3722   if(nr == 0) // [HGM] dual: markers only on left board\r
3723   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3724     for (column = 0; column < BOARD_WIDTH; column++) {\r
3725         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3726             HBRUSH oldBrush = SelectObject(hdcmem, \r
3727                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3728             SquareToPos(row, column, &x, &y);\r
3729             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3730                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3731             SelectObject(hdcmem, oldBrush);\r
3732         }\r
3733     }\r
3734   }\r
3735   if(logoHeight) {\r
3736         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3737         if(appData.autoLogo) {\r
3738           \r
3739           switch(gameMode) { // pick logos based on game mode\r
3740             case IcsObserving:\r
3741                 whiteLogo = second.programLogo; // ICS logo\r
3742                 blackLogo = second.programLogo;\r
3743             default:\r
3744                 break;\r
3745             case IcsPlayingWhite:\r
3746                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3747                 blackLogo = second.programLogo; // ICS logo\r
3748                 break;\r
3749             case IcsPlayingBlack:\r
3750                 whiteLogo = second.programLogo; // ICS logo\r
3751                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3752                 break;\r
3753             case TwoMachinesPlay:\r
3754                 if(first.twoMachinesColor[0] == 'b') {\r
3755                     whiteLogo = second.programLogo;\r
3756                     blackLogo = first.programLogo;\r
3757                 }\r
3758                 break;\r
3759             case MachinePlaysWhite:\r
3760                 blackLogo = userLogo;\r
3761                 break;\r
3762             case MachinePlaysBlack:\r
3763                 whiteLogo = userLogo;\r
3764                 blackLogo = first.programLogo;\r
3765           }\r
3766         }\r
3767         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3768         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3769   }\r
3770 \r
3771   if( appData.highlightMoveWithArrow ) {\r
3772     DrawArrowHighlight(hdcmem);\r
3773   }\r
3774 \r
3775   DrawCoordsOnDC(hdcmem);\r
3776 \r
3777   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3778                  /* to make sure lastDrawn contains what is actually drawn */\r
3779 \r
3780   /* Put the dragged piece back into place and draw it (out of place!) */\r
3781     if (dragged_piece != EmptySquare) {\r
3782     /* [HGM] or restack */\r
3783     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3784                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3785     else\r
3786     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3787                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3788     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3789     x = dragInfo.pos.x - squareSize / 2;\r
3790     y = dragInfo.pos.y - squareSize / 2;\r
3791     DrawPieceOnDC(hdcmem, dragged_piece,\r
3792                   ((int) dragged_piece < (int) BlackPawn), \r
3793                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3794   }   \r
3795   \r
3796   /* Put the animated piece back into place and draw it */\r
3797   if (animInfo.piece != EmptySquare) {\r
3798     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3799     x = boardRect.left + animInfo.pos.x;\r
3800     y = boardRect.top + animInfo.pos.y;\r
3801     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3802                   ((int) animInfo.piece < (int) BlackPawn),\r
3803                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3804   }\r
3805 \r
3806   /* Release the bufferBitmap by selecting in the old bitmap \r
3807    * and delete the memory DC\r
3808    */\r
3809   SelectObject(hdcmem, oldBitmap);\r
3810   DeleteDC(hdcmem);\r
3811 \r
3812   /* Set clipping on the target DC */\r
3813   if (!fullrepaint) {\r
3814     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3815         RECT rect;\r
3816         GetRgnBox(clips[x], &rect);\r
3817         DeleteObject(clips[x]);\r
3818         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3819                           rect.right + wpMain.width/2, rect.bottom);\r
3820     }\r
3821     SelectClipRgn(hdc, clips[0]);\r
3822     for (x = 1; x < num_clips; x++) {\r
3823       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3824         abort();   // this should never ever happen!\r
3825     } \r
3826   }\r
3827 \r
3828   /* Copy the new bitmap onto the screen in one go.\r
3829    * This way we avoid any flickering\r
3830    */\r
3831   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3832   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3833          boardRect.right - boardRect.left,\r
3834          boardRect.bottom - boardRect.top,\r
3835          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3836   if(saveDiagFlag) { \r
3837     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
3838     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3839 \r
3840     GetObject(bufferBitmap, sizeof(b), &b);\r
3841     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
3842         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3843         bih.biWidth = b.bmWidth;\r
3844         bih.biHeight = b.bmHeight;\r
3845         bih.biPlanes = 1;\r
3846         bih.biBitCount = b.bmBitsPixel;\r
3847         bih.biCompression = 0;\r
3848         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3849         bih.biXPelsPerMeter = 0;\r
3850         bih.biYPelsPerMeter = 0;\r
3851         bih.biClrUsed = 0;\r
3852         bih.biClrImportant = 0;\r
3853 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3854 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3855         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3856 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3857 \r
3858         wb = b.bmWidthBytes;\r
3859         // count colors\r
3860         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3861                 int k = ((int*) pData)[i];\r
3862                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3863                 if(j >= 16) break;\r
3864                 color[j] = k;\r
3865                 if(j >= nrColors) nrColors = j+1;\r
3866         }\r
3867         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3868                 INT p = 0;\r
3869                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3870                     for(w=0; w<(wb>>2); w+=2) {\r
3871                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3872                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3873                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3874                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3875                         pData[p++] = m | j<<4;\r
3876                     }\r
3877                     while(p&3) pData[p++] = 0;\r
3878                 }\r
3879                 fac = 3;\r
3880                 wb = ((wb+31)>>5)<<2;\r
3881         }\r
3882         // write BITMAPFILEHEADER\r
3883         fprintf(diagFile, "BM");\r
3884         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3885         fputDW(diagFile, 0);\r
3886         fputDW(diagFile, 0x36 + (fac?64:0));\r
3887         // write BITMAPINFOHEADER\r
3888         fputDW(diagFile, 40);\r
3889         fputDW(diagFile, b.bmWidth);\r
3890         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3891         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3892         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3893         fputDW(diagFile, 0);\r
3894         fputDW(diagFile, 0);\r
3895         fputDW(diagFile, 0);\r
3896         fputDW(diagFile, 0);\r
3897         fputDW(diagFile, 0);\r
3898         fputDW(diagFile, 0);\r
3899         // write color table\r
3900         if(fac)\r
3901         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3902         // write bitmap data\r
3903         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3904                 fputc(pData[i], diagFile);\r
3905      }\r
3906   }\r
3907 \r
3908   SelectObject(tmphdc, oldBitmap);\r
3909 \r
3910   /* Massive cleanup */\r
3911   for (x = 0; x < num_clips; x++)\r
3912     DeleteObject(clips[x]);\r
3913 \r
3914   DeleteDC(tmphdc);\r
3915   DeleteObject(bufferBitmap);\r
3916 \r
3917   if (releaseDC) \r
3918     ReleaseDC(hwndMain, hdc);\r
3919   \r
3920   if (lastDrawnFlipView != flipView && nr == 0) {\r
3921     if (flipView)\r
3922       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3923     else\r
3924       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3925   }\r
3926 \r
3927 /*  CopyBoard(lastDrawn, board);*/\r
3928   lastDrawnHighlight = highlightInfo;\r
3929   lastDrawnPremove   = premoveHighlightInfo;\r
3930   lastDrawnFlipView = flipView;\r
3931   lastDrawnValid[nr] = 1;\r
3932 }\r
3933 \r
3934 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3935 int\r
3936 SaveDiagram(f)\r
3937      FILE *f;\r
3938 {\r
3939     saveDiagFlag = 1; diagFile = f;\r
3940     HDCDrawPosition(NULL, TRUE, NULL);\r
3941 \r
3942     saveDiagFlag = 0;\r
3943 \r
3944 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
3945     \r
3946     fclose(f);\r
3947     return TRUE;\r
3948 }\r
3949 \r
3950 \r
3951 /*---------------------------------------------------------------------------*\\r
3952 | CLIENT PAINT PROCEDURE\r
3953 |   This is the main event-handler for the WM_PAINT message.\r
3954 |\r
3955 \*---------------------------------------------------------------------------*/\r
3956 VOID\r
3957 PaintProc(HWND hwnd)\r
3958 {\r
3959   HDC         hdc;\r
3960   PAINTSTRUCT ps;\r
3961   HFONT       oldFont;\r
3962 \r
3963   if((hdc = BeginPaint(hwnd, &ps))) {\r
3964     if (IsIconic(hwnd)) {\r
3965       DrawIcon(hdc, 2, 2, iconCurrent);\r
3966     } else {\r
3967       if (!appData.monoMode) {\r
3968         SelectPalette(hdc, hPal, FALSE);\r
3969         RealizePalette(hdc);\r
3970       }\r
3971       HDCDrawPosition(hdc, 1, NULL);\r
3972       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
3973         flipView = !flipView; partnerUp = !partnerUp;\r
3974         HDCDrawPosition(hdc, 1, NULL);\r
3975         flipView = !flipView; partnerUp = !partnerUp;\r
3976       }\r
3977       oldFont =\r
3978         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3979       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
3980                  ETO_CLIPPED|ETO_OPAQUE,\r
3981                  &messageRect, messageText, strlen(messageText), NULL);\r
3982       SelectObject(hdc, oldFont);\r
3983       DisplayBothClocks();\r
3984     }\r
3985     EndPaint(hwnd,&ps);\r
3986   }\r
3987 \r
3988   return;\r
3989 }\r
3990 \r
3991 \r
3992 /*\r
3993  * If the user selects on a border boundary, return -1; if off the board,\r
3994  *   return -2.  Otherwise map the event coordinate to the square.\r
3995  * The offset boardRect.left or boardRect.top must already have been\r
3996  *   subtracted from x.\r
3997  */\r
3998 int EventToSquare(x, limit)\r
3999      int x, limit;\r
4000 {\r
4001   if (x <= 0)\r
4002     return -2;\r
4003   if (x < lineGap)\r
4004     return -1;\r
4005   x -= lineGap;\r
4006   if ((x % (squareSize + lineGap)) >= squareSize)\r
4007     return -1;\r
4008   x /= (squareSize + lineGap);\r
4009     if (x >= limit)\r
4010     return -2;\r
4011   return x;\r
4012 }\r
4013 \r
4014 typedef struct {\r
4015   char piece;\r
4016   int command;\r
4017   char* name;\r
4018 } DropEnable;\r
4019 \r
4020 DropEnable dropEnables[] = {\r
4021   { 'P', DP_Pawn, N_("Pawn") },\r
4022   { 'N', DP_Knight, N_("Knight") },\r
4023   { 'B', DP_Bishop, N_("Bishop") },\r
4024   { 'R', DP_Rook, N_("Rook") },\r
4025   { 'Q', DP_Queen, N_("Queen") },\r
4026 };\r
4027 \r
4028 VOID\r
4029 SetupDropMenu(HMENU hmenu)\r
4030 {\r
4031   int i, count, enable;\r
4032   char *p;\r
4033   extern char white_holding[], black_holding[];\r
4034   char item[MSG_SIZ];\r
4035 \r
4036   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4037     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4038                dropEnables[i].piece);\r
4039     count = 0;\r
4040     while (p && *p++ == dropEnables[i].piece) count++;\r
4041       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4042     enable = count > 0 || !appData.testLegality\r
4043       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4044                       && !appData.icsActive);\r
4045     ModifyMenu(hmenu, dropEnables[i].command,\r
4046                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4047                dropEnables[i].command, item);\r
4048   }\r
4049 }\r
4050 \r
4051 void DragPieceBegin(int x, int y)\r
4052 {\r
4053       dragInfo.lastpos.x = boardRect.left + x;\r
4054       dragInfo.lastpos.y = boardRect.top + y;\r
4055       dragInfo.from.x = fromX;\r
4056       dragInfo.from.y = fromY;\r
4057       dragInfo.start = dragInfo.from;\r
4058       SetCapture(hwndMain);\r
4059 }\r
4060 \r
4061 void DragPieceEnd(int x, int y)\r
4062 {\r
4063     ReleaseCapture();\r
4064     dragInfo.start.x = dragInfo.start.y = -1;\r
4065     dragInfo.from = dragInfo.start;\r
4066     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4067 }\r
4068 \r
4069 /* Event handler for mouse messages */\r
4070 VOID\r
4071 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4072 {\r
4073   int x, y, menuNr;\r
4074   POINT pt;\r
4075   static int recursive = 0;\r
4076   HMENU hmenu;\r
4077   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4078 \r
4079   if (recursive) {\r
4080     if (message == WM_MBUTTONUP) {\r
4081       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4082          to the middle button: we simulate pressing the left button too!\r
4083          */\r
4084       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4085       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4086     }\r
4087     return;\r
4088   }\r
4089   recursive++;\r
4090   \r
4091   pt.x = LOWORD(lParam);\r
4092   pt.y = HIWORD(lParam);\r
4093   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4094   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4095   if (!flipView && y >= 0) {\r
4096     y = BOARD_HEIGHT - 1 - y;\r
4097   }\r
4098   if (flipView && x >= 0) {\r
4099     x = BOARD_WIDTH - 1 - x;\r
4100   }\r
4101 \r
4102   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4103 \r
4104   switch (message) {\r
4105   case WM_LBUTTONDOWN:\r
4106       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4107         if (gameMode == EditPosition) {\r
4108           SetWhiteToPlayEvent();\r
4109         } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {\r
4110           AdjustClock(flipClock, -1);\r
4111         } else if (gameMode == IcsPlayingBlack ||\r
4112                    gameMode == MachinePlaysWhite) {\r
4113           CallFlagEvent();\r
4114         }\r
4115       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4116         if (gameMode == EditPosition) {\r
4117           SetBlackToPlayEvent();\r
4118         } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {\r
4119           AdjustClock(!flipClock, -1);\r
4120         } else if (gameMode == IcsPlayingWhite ||\r
4121                    gameMode == MachinePlaysBlack) {\r
4122           CallFlagEvent();\r
4123         }\r
4124       }\r
4125       dragInfo.start.x = dragInfo.start.y = -1;\r
4126       dragInfo.from = dragInfo.start;\r
4127     if(fromX == -1 && frozen) { // not sure where this is for\r
4128                 fromX = fromY = -1; \r
4129       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4130       break;\r
4131     }\r
4132       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4133       DrawPosition(TRUE, NULL);\r
4134     break;\r
4135 \r
4136   case WM_LBUTTONUP:\r
4137       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4138       DrawPosition(TRUE, NULL);\r
4139     break;\r
4140 \r
4141   case WM_MOUSEMOVE:\r
4142     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4143     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4144     if ((appData.animateDragging || appData.highlightDragging)\r
4145         && (wParam & MK_LBUTTON)\r
4146         && dragInfo.from.x >= 0) \r
4147     {\r
4148       BOOL full_repaint = FALSE;\r
4149 \r
4150       if (appData.animateDragging) {\r
4151         dragInfo.pos = pt;\r
4152       }\r
4153       if (appData.highlightDragging) {\r
4154         SetHighlights(fromX, fromY, x, y);\r
4155         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4156             full_repaint = TRUE;\r
4157         }\r
4158       }\r
4159       \r
4160       DrawPosition( full_repaint, NULL);\r
4161       \r
4162       dragInfo.lastpos = dragInfo.pos;\r
4163     }\r
4164     break;\r
4165 \r
4166   case WM_MOUSEWHEEL: // [DM]\r
4167     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4168        /* Mouse Wheel is being rolled forward\r
4169         * Play moves forward\r
4170         */\r
4171        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4172                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4173        /* Mouse Wheel is being rolled backward\r
4174         * Play moves backward\r
4175         */\r
4176        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4177                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4178     }\r
4179     break;\r
4180 \r
4181   case WM_MBUTTONUP:\r
4182   case WM_RBUTTONUP:\r
4183     ReleaseCapture();\r
4184     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4185     break;\r
4186  \r
4187   case WM_MBUTTONDOWN:\r
4188   case WM_RBUTTONDOWN:\r
4189     ErrorPopDown();\r
4190     ReleaseCapture();\r
4191     fromX = fromY = -1;\r
4192     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4193     dragInfo.start.x = dragInfo.start.y = -1;\r
4194     dragInfo.from = dragInfo.start;\r
4195     dragInfo.lastpos = dragInfo.pos;\r
4196     if (appData.highlightDragging) {\r
4197       ClearHighlights();\r
4198     }\r
4199     if(y == -2) {\r
4200       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4201       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4202           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4203       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4204           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4205       }\r
4206       break;\r
4207     }\r
4208     DrawPosition(TRUE, NULL);\r
4209 \r
4210     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4211     switch (menuNr) {\r
4212     case 0:\r
4213       if (message == WM_MBUTTONDOWN) {\r
4214         buttonCount = 3;  /* even if system didn't think so */\r
4215         if (wParam & MK_SHIFT) \r
4216           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4217         else\r
4218           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4219       } else { /* message == WM_RBUTTONDOWN */\r
4220         /* Just have one menu, on the right button.  Windows users don't\r
4221            think to try the middle one, and sometimes other software steals\r
4222            it, or it doesn't really exist. */\r
4223         if(gameInfo.variant != VariantShogi)\r
4224             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4225         else\r
4226             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4227       }\r
4228       break;\r
4229     case 2:\r
4230       SetCapture(hwndMain);
4231       break;\r
4232     case 1:\r
4233       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4234       SetupDropMenu(hmenu);\r
4235       MenuPopup(hwnd, pt, hmenu, -1);\r
4236     default:\r
4237       break;\r
4238     }\r
4239     break;\r
4240   }\r
4241 \r
4242   recursive--;\r
4243 }\r
4244 \r
4245 /* Preprocess messages for buttons in main window */\r
4246 LRESULT CALLBACK\r
4247 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4248 {\r
4249   int id = GetWindowLong(hwnd, GWL_ID);\r
4250   int i, dir;\r
4251 \r
4252   for (i=0; i<N_BUTTONS; i++) {\r
4253     if (buttonDesc[i].id == id) break;\r
4254   }\r
4255   if (i == N_BUTTONS) return 0;\r
4256   switch (message) {\r
4257   case WM_KEYDOWN:\r
4258     switch (wParam) {\r
4259     case VK_LEFT:\r
4260     case VK_RIGHT:\r
4261       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4262       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4263       return TRUE;\r
4264     }\r
4265     break;\r
4266   case WM_CHAR:\r
4267     switch (wParam) {\r
4268     case '\r':\r
4269       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4270       return TRUE;\r
4271     default:\r
4272       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4273         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4274         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4275         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4276         SetFocus(h);\r
4277         SendMessage(h, WM_CHAR, wParam, lParam);\r
4278         return TRUE;\r
4279       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4280         PopUpMoveDialog((char)wParam);\r
4281       }\r
4282       break;\r
4283     }\r
4284     break;\r
4285   }\r
4286   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4287 }\r
4288 \r
4289 /* Process messages for Promotion dialog box */\r
4290 LRESULT CALLBACK\r
4291 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4292 {\r
4293   char promoChar;\r
4294 \r
4295   switch (message) {\r
4296   case WM_INITDIALOG: /* message: initialize dialog box */\r
4297     /* Center the dialog over the application window */\r
4298     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4299     Translate(hDlg, DLG_PromotionKing);\r
4300     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4301       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4302        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4303                SW_SHOW : SW_HIDE);\r
4304     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4305     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4306        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4307          PieceToChar(WhiteAngel) != '~') ||\r
4308         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4309          PieceToChar(BlackAngel) != '~')   ) ?\r
4310                SW_SHOW : SW_HIDE);\r
4311     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4312        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4313          PieceToChar(WhiteMarshall) != '~') ||\r
4314         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4315          PieceToChar(BlackMarshall) != '~')   ) ?\r
4316                SW_SHOW : SW_HIDE);\r
4317     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4318     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4319        gameInfo.variant != VariantShogi ?\r
4320                SW_SHOW : SW_HIDE);\r
4321     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4322        gameInfo.variant != VariantShogi ?\r
4323                SW_SHOW : SW_HIDE);\r
4324     if(gameInfo.variant == VariantShogi) {\r
4325         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4326         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4327         SetWindowText(hDlg, "Promote?");\r
4328     }\r
4329     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4330        gameInfo.variant == VariantSuper ?\r
4331                SW_SHOW : SW_HIDE);\r
4332     return TRUE;\r
4333 \r
4334   case WM_COMMAND: /* message: received a command */\r
4335     switch (LOWORD(wParam)) {\r
4336     case IDCANCEL:\r
4337       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4338       ClearHighlights();\r
4339       DrawPosition(FALSE, NULL);\r
4340       return TRUE;\r
4341     case PB_King:\r
4342       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4343       break;\r
4344     case PB_Queen:\r
4345       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4346       break;\r
4347     case PB_Rook:\r
4348       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4349       break;\r
4350     case PB_Bishop:\r
4351       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4352       break;\r
4353     case PB_Chancellor:\r
4354       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4355       break;\r
4356     case PB_Archbishop:\r
4357       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4358       break;\r
4359     case PB_Knight:\r
4360       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4361       break;\r
4362     default:\r
4363       return FALSE;\r
4364     }\r
4365     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4366     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4367     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4368     fromX = fromY = -1;\r
4369     if (!appData.highlightLastMove) {\r
4370       ClearHighlights();\r
4371       DrawPosition(FALSE, NULL);\r
4372     }\r
4373     return TRUE;\r
4374   }\r
4375   return FALSE;\r
4376 }\r
4377 \r
4378 /* Pop up promotion dialog */\r
4379 VOID\r
4380 PromotionPopup(HWND hwnd)\r
4381 {\r
4382   FARPROC lpProc;\r
4383 \r
4384   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4385   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4386     hwnd, (DLGPROC)lpProc);\r
4387   FreeProcInstance(lpProc);\r
4388 }\r
4389 \r
4390 void\r
4391 PromotionPopUp()\r
4392 {\r
4393   DrawPosition(TRUE, NULL);\r
4394   PromotionPopup(hwndMain);\r
4395 }\r
4396 \r
4397 /* Toggle ShowThinking */\r
4398 VOID\r
4399 ToggleShowThinking()\r
4400 {\r
4401   appData.showThinking = !appData.showThinking;\r
4402   ShowThinkingEvent();\r
4403 }\r
4404 \r
4405 VOID\r
4406 LoadGameDialog(HWND hwnd, char* title)\r
4407 {\r
4408   UINT number = 0;\r
4409   FILE *f;\r
4410   char fileTitle[MSG_SIZ];\r
4411   f = OpenFileDialog(hwnd, "rb", "",\r
4412                      appData.oldSaveStyle ? "gam" : "pgn",\r
4413                      GAME_FILT,\r
4414                      title, &number, fileTitle, NULL);\r
4415   if (f != NULL) {\r
4416     cmailMsgLoaded = FALSE;\r
4417     if (number == 0) {\r
4418       int error = GameListBuild(f);\r
4419       if (error) {\r
4420         DisplayError(_("Cannot build game list"), error);\r
4421       } else if (!ListEmpty(&gameList) &&\r
4422                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4423         GameListPopUp(f, fileTitle);\r
4424         return;\r
4425       }\r
4426       GameListDestroy();\r
4427       number = 1;\r
4428     }\r
4429     LoadGame(f, number, fileTitle, FALSE);\r
4430   }\r
4431 }\r
4432 \r
4433 int get_term_width()\r
4434 {\r
4435     HDC hdc;\r
4436     TEXTMETRIC tm;\r
4437     RECT rc;\r
4438     HFONT hfont, hold_font;\r
4439     LOGFONT lf;\r
4440     HWND hText;\r
4441 \r
4442     if (hwndConsole)\r
4443         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4444     else\r
4445         return 79;\r
4446 \r
4447     // get the text metrics\r
4448     hdc = GetDC(hText);\r
4449     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4450     if (consoleCF.dwEffects & CFE_BOLD)\r
4451         lf.lfWeight = FW_BOLD;\r
4452     if (consoleCF.dwEffects & CFE_ITALIC)\r
4453         lf.lfItalic = TRUE;\r
4454     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4455         lf.lfStrikeOut = TRUE;\r
4456     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4457         lf.lfUnderline = TRUE;\r
4458     hfont = CreateFontIndirect(&lf);\r
4459     hold_font = SelectObject(hdc, hfont);\r
4460     GetTextMetrics(hdc, &tm);\r
4461     SelectObject(hdc, hold_font);\r
4462     DeleteObject(hfont);\r
4463     ReleaseDC(hText, hdc);\r
4464 \r
4465     // get the rectangle\r
4466     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4467 \r
4468     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4469 }\r
4470 \r
4471 void UpdateICSWidth(HWND hText)\r
4472 {\r
4473     LONG old_width, new_width;\r
4474 \r
4475     new_width = get_term_width(hText, FALSE);\r
4476     old_width = GetWindowLong(hText, GWL_USERDATA);\r
4477     if (new_width != old_width)\r
4478     {\r
4479         ics_update_width(new_width);\r
4480         SetWindowLong(hText, GWL_USERDATA, new_width);\r
4481     }\r
4482 }\r
4483 \r
4484 VOID\r
4485 ChangedConsoleFont()\r
4486 {\r
4487   CHARFORMAT cfmt;\r
4488   CHARRANGE tmpsel, sel;\r
4489   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4490   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4491   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4492   PARAFORMAT paraf;\r
4493 \r
4494   cfmt.cbSize = sizeof(CHARFORMAT);\r
4495   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4496     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4497                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4498   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4499    * size.  This was undocumented in the version of MSVC++ that I had\r
4500    * when I wrote the code, but is apparently documented now.\r
4501    */\r
4502   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4503   cfmt.bCharSet = f->lf.lfCharSet;\r
4504   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4505   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4506   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4507   /* Why are the following seemingly needed too? */\r
4508   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4509   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4510   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4511   tmpsel.cpMin = 0;\r
4512   tmpsel.cpMax = -1; /*999999?*/\r
4513   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4514   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4515   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4516    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4517    */\r
4518   paraf.cbSize = sizeof(paraf);\r
4519   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4520   paraf.dxStartIndent = 0;\r
4521   paraf.dxOffset = WRAP_INDENT;\r
4522   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4523   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4524   UpdateICSWidth(hText);\r
4525 }\r
4526 \r
4527 /*---------------------------------------------------------------------------*\\r
4528  *\r
4529  * Window Proc for main window\r
4530  *\r
4531 \*---------------------------------------------------------------------------*/\r
4532 \r
4533 /* Process messages for main window, etc. */\r
4534 LRESULT CALLBACK\r
4535 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4536 {\r
4537   FARPROC lpProc;\r
4538   int wmId, wmEvent;\r
4539   char *defName;\r
4540   FILE *f;\r
4541   UINT number;\r
4542   char fileTitle[MSG_SIZ];\r
4543   char buf[MSG_SIZ];\r
4544   static SnapData sd;\r
4545 \r
4546   switch (message) {\r
4547 \r
4548   case WM_PAINT: /* message: repaint portion of window */\r
4549     PaintProc(hwnd);\r
4550     break;\r
4551 \r
4552   case WM_ERASEBKGND:\r
4553     if (IsIconic(hwnd)) {\r
4554       /* Cheat; change the message */\r
4555       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4556     } else {\r
4557       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4558     }\r
4559     break;\r
4560 \r
4561   case WM_LBUTTONDOWN:\r
4562   case WM_MBUTTONDOWN:\r
4563   case WM_RBUTTONDOWN:\r
4564   case WM_LBUTTONUP:\r
4565   case WM_MBUTTONUP:\r
4566   case WM_RBUTTONUP:\r
4567   case WM_MOUSEMOVE:\r
4568   case WM_MOUSEWHEEL:\r
4569     MouseEvent(hwnd, message, wParam, lParam);\r
4570     break;\r
4571 \r
4572   JAWS_KB_NAVIGATION\r
4573 \r
4574   case WM_CHAR:\r
4575     \r
4576     JAWS_ALT_INTERCEPT\r
4577 \r
4578     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4579         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4580         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4581         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4582         SetFocus(h);\r
4583         SendMessage(h, message, wParam, lParam);\r
4584     } else if(lParam != KF_REPEAT) {\r
4585         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4586                 PopUpMoveDialog((char)wParam);\r
4587         } else if((char)wParam == 003) CopyGameToClipboard();\r
4588          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4589     }\r
4590 \r
4591     break;\r
4592 \r
4593   case WM_PALETTECHANGED:\r
4594     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4595       int nnew;\r
4596       HDC hdc = GetDC(hwndMain);\r
4597       SelectPalette(hdc, hPal, TRUE);\r
4598       nnew = RealizePalette(hdc);\r
4599       if (nnew > 0) {\r
4600         paletteChanged = TRUE;\r
4601         InvalidateRect(hwnd, &boardRect, FALSE);\r
4602       }\r
4603       ReleaseDC(hwnd, hdc);\r
4604     }\r
4605     break;\r
4606 \r
4607   case WM_QUERYNEWPALETTE:\r
4608     if (!appData.monoMode /*&& paletteChanged*/) {\r
4609       int nnew;\r
4610       HDC hdc = GetDC(hwndMain);\r
4611       paletteChanged = FALSE;\r
4612       SelectPalette(hdc, hPal, FALSE);\r
4613       nnew = RealizePalette(hdc);\r
4614       if (nnew > 0) {\r
4615         InvalidateRect(hwnd, &boardRect, FALSE);\r
4616       }\r
4617       ReleaseDC(hwnd, hdc);\r
4618       return TRUE;\r
4619     }\r
4620     return FALSE;\r
4621 \r
4622   case WM_COMMAND: /* message: command from application menu */\r
4623     wmId    = LOWORD(wParam);\r
4624     wmEvent = HIWORD(wParam);\r
4625 \r
4626     switch (wmId) {\r
4627     case IDM_NewGame:\r
4628       ResetGameEvent();\r
4629       SAY("new game enter a move to play against the computer with white");\r
4630       break;\r
4631 \r
4632     case IDM_NewGameFRC:\r
4633       if( NewGameFRC() == 0 ) {\r
4634         ResetGameEvent();\r
4635       }\r
4636       break;\r
4637 \r
4638     case IDM_NewVariant:\r
4639       NewVariantPopup(hwnd);\r
4640       break;\r
4641 \r
4642     case IDM_LoadGame:\r
4643       LoadGameDialog(hwnd, _("Load Game from File"));\r
4644       break;\r
4645 \r
4646     case IDM_LoadNextGame:\r
4647       ReloadGame(1);\r
4648       break;\r
4649 \r
4650     case IDM_LoadPrevGame:\r
4651       ReloadGame(-1);\r
4652       break;\r
4653 \r
4654     case IDM_ReloadGame:\r
4655       ReloadGame(0);\r
4656       break;\r
4657 \r
4658     case IDM_LoadPosition:\r
4659       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4660         Reset(FALSE, TRUE);\r
4661       }\r
4662       number = 1;\r
4663       f = OpenFileDialog(hwnd, "rb", "",\r
4664                          appData.oldSaveStyle ? "pos" : "fen",\r
4665                          POSITION_FILT,\r
4666                          _("Load Position from File"), &number, fileTitle, NULL);\r
4667       if (f != NULL) {\r
4668         LoadPosition(f, number, fileTitle);\r
4669       }\r
4670       break;\r
4671 \r
4672     case IDM_LoadNextPosition:\r
4673       ReloadPosition(1);\r
4674       break;\r
4675 \r
4676     case IDM_LoadPrevPosition:\r
4677       ReloadPosition(-1);\r
4678       break;\r
4679 \r
4680     case IDM_ReloadPosition:\r
4681       ReloadPosition(0);\r
4682       break;\r
4683 \r
4684     case IDM_SaveGame:\r
4685       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4686       f = OpenFileDialog(hwnd, "a", defName,\r
4687                          appData.oldSaveStyle ? "gam" : "pgn",\r
4688                          GAME_FILT,\r
4689                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4690       if (f != NULL) {\r
4691         SaveGame(f, 0, "");\r
4692       }\r
4693       break;\r
4694 \r
4695     case IDM_SavePosition:\r
4696       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4697       f = OpenFileDialog(hwnd, "a", defName,\r
4698                          appData.oldSaveStyle ? "pos" : "fen",\r
4699                          POSITION_FILT,\r
4700                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4701       if (f != NULL) {\r
4702         SavePosition(f, 0, "");\r
4703       }\r
4704       break;\r
4705 \r
4706     case IDM_SaveDiagram:\r
4707       defName = "diagram";\r
4708       f = OpenFileDialog(hwnd, "wb", defName,\r
4709                          "bmp",\r
4710                          DIAGRAM_FILT,\r
4711                          "Save Diagram to File", NULL, fileTitle, NULL);\r
4712       if (f != NULL) {\r
4713         SaveDiagram(f);\r
4714       }\r
4715       break;\r
4716 \r
4717     case IDM_CopyGame:\r
4718       CopyGameToClipboard();\r
4719       break;\r
4720 \r
4721     case IDM_PasteGame:\r
4722       PasteGameFromClipboard();\r
4723       break;\r
4724 \r
4725     case IDM_CopyGameListToClipboard:\r
4726       CopyGameListToClipboard();\r
4727       break;\r
4728 \r
4729     /* [AS] Autodetect FEN or PGN data */\r
4730     case IDM_PasteAny:\r
4731       PasteGameOrFENFromClipboard();\r
4732       break;\r
4733 \r
4734     /* [AS] Move history */\r
4735     case IDM_ShowMoveHistory:\r
4736         if( MoveHistoryIsUp() ) {\r
4737             MoveHistoryPopDown();\r
4738         }\r
4739         else {\r
4740             MoveHistoryPopUp();\r
4741         }\r
4742         break;\r
4743 \r
4744     /* [AS] Eval graph */\r
4745     case IDM_ShowEvalGraph:\r
4746         if( EvalGraphIsUp() ) {\r
4747             EvalGraphPopDown();\r
4748         }\r
4749         else {\r
4750             EvalGraphPopUp();\r
4751             SetFocus(hwndMain);\r
4752         }\r
4753         break;\r
4754 \r
4755     /* [AS] Engine output */\r
4756     case IDM_ShowEngineOutput:\r
4757         if( EngineOutputIsUp() ) {\r
4758             EngineOutputPopDown();\r
4759         }\r
4760         else {\r
4761             EngineOutputPopUp();\r
4762         }\r
4763         break;\r
4764 \r
4765     /* [AS] User adjudication */\r
4766     case IDM_UserAdjudication_White:\r
4767         UserAdjudicationEvent( +1 );\r
4768         break;\r
4769 \r
4770     case IDM_UserAdjudication_Black:\r
4771         UserAdjudicationEvent( -1 );\r
4772         break;\r
4773 \r
4774     case IDM_UserAdjudication_Draw:\r
4775         UserAdjudicationEvent( 0 );\r
4776         break;\r
4777 \r
4778     /* [AS] Game list options dialog */\r
4779     case IDM_GameListOptions:\r
4780       GameListOptions();\r
4781       break;\r
4782 \r
4783     case IDM_NewChat:\r
4784       ChatPopUp(NULL);\r
4785       break;\r
4786 \r
4787     case IDM_CopyPosition:\r
4788       CopyFENToClipboard();\r
4789       break;\r
4790 \r
4791     case IDM_PastePosition:\r
4792       PasteFENFromClipboard();\r
4793       break;\r
4794 \r
4795     case IDM_MailMove:\r
4796       MailMoveEvent();\r
4797       break;\r
4798 \r
4799     case IDM_ReloadCMailMsg:\r
4800       Reset(TRUE, TRUE);\r
4801       ReloadCmailMsgEvent(FALSE);\r
4802       break;\r
4803 \r
4804     case IDM_Minimize:\r
4805       ShowWindow(hwnd, SW_MINIMIZE);\r
4806       break;\r
4807 \r
4808     case IDM_Exit:\r
4809       ExitEvent(0);\r
4810       break;\r
4811 \r
4812     case IDM_MachineWhite:\r
4813       MachineWhiteEvent();\r
4814       /*\r
4815        * refresh the tags dialog only if it's visible\r
4816        */\r
4817       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4818           char *tags;\r
4819           tags = PGNTags(&gameInfo);\r
4820           TagsPopUp(tags, CmailMsg());\r
4821           free(tags);\r
4822       }\r
4823       SAY("computer starts playing white");\r
4824       break;\r
4825 \r
4826     case IDM_MachineBlack:\r
4827       MachineBlackEvent();\r
4828       /*\r
4829        * refresh the tags dialog only if it's visible\r
4830        */\r
4831       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4832           char *tags;\r
4833           tags = PGNTags(&gameInfo);\r
4834           TagsPopUp(tags, CmailMsg());\r
4835           free(tags);\r
4836       }\r
4837       SAY("computer starts playing black");\r
4838       break;\r
4839 \r
4840     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4841       if(gameMode != BeginningOfGame) { // allow menu item to remain enabled for better mode highligting\r
4842         DisplayError(_("You can only start a match from the initial position."), 0); break;\r
4843       }\r
4844       matchMode = 2;// distinguish from command-line-triggered case (matchMode=1)\r
4845       appData.matchGames = appData.defaultMatchGames;\r
4846       matchGame = 1;\r
4847 \r
4848     case IDM_TwoMachines:\r
4849       TwoMachinesEvent();\r
4850       /*\r
4851        * refresh the tags dialog only if it's visible\r
4852        */\r
4853       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4854           char *tags;\r
4855           tags = PGNTags(&gameInfo);\r
4856           TagsPopUp(tags, CmailMsg());\r
4857           free(tags);\r
4858       }\r
4859       SAY("computer starts playing both sides");\r
4860       break;\r
4861 \r
4862     case IDM_AnalysisMode:\r
4863       if (!first.analysisSupport) {\r
4864         snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4865         DisplayError(buf, 0);\r
4866       } else {\r
4867         SAY("analyzing current position");\r
4868         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4869         if (appData.icsActive) {\r
4870                if (gameMode != IcsObserving) {\r
4871                  snprintf(buf, MSG_SIZ, "You are not observing a game");\r
4872                        DisplayError(buf, 0);\r
4873                        /* secure check */\r
4874                        if (appData.icsEngineAnalyze) {\r
4875                                if (appData.debugMode) \r
4876                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4877                                ExitAnalyzeMode();\r
4878                                ModeHighlight();\r
4879                                break;\r
4880                        }\r
4881                        break;\r
4882                } else {\r
4883                        /* if enable, user want disable icsEngineAnalyze */\r
4884                        if (appData.icsEngineAnalyze) {\r
4885                                ExitAnalyzeMode();\r
4886                                ModeHighlight();\r
4887                                break;\r
4888                        }\r
4889                        appData.icsEngineAnalyze = TRUE;\r
4890                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4891                }\r
4892         } \r
4893         if (!appData.showThinking) ToggleShowThinking();\r
4894         AnalyzeModeEvent();\r
4895       }\r
4896       break;\r
4897 \r
4898     case IDM_AnalyzeFile:\r
4899       if (!first.analysisSupport) {\r
4900         char buf[MSG_SIZ];\r
4901           snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4902         DisplayError(buf, 0);\r
4903       } else {\r
4904         if (!appData.showThinking) ToggleShowThinking();\r
4905         AnalyzeFileEvent();\r
4906         LoadGameDialog(hwnd, _("Analyze Game from File"));\r
4907         AnalysisPeriodicEvent(1);\r
4908       }\r
4909       break;\r
4910 \r
4911     case IDM_IcsClient:\r
4912       IcsClientEvent();\r
4913       break;\r
4914 \r
4915     case IDM_EditGame:\r
4916     case IDM_EditGame2:\r
4917       EditGameEvent();\r
4918       SAY("edit game");\r
4919       break;\r
4920 \r
4921     case IDM_EditPosition:\r
4922     case IDM_EditPosition2:\r
4923       EditPositionEvent();\r
4924       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4925       break;\r
4926 \r
4927     case IDM_Training:\r
4928       TrainingEvent();\r
4929       break;\r
4930 \r
4931     case IDM_ShowGameList:\r
4932       ShowGameListProc();\r
4933       break;\r
4934 \r
4935     case IDM_EditProgs1:\r
4936       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
4937       break;\r
4938 \r
4939     case IDM_EditProgs2:\r
4940       EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);\r
4941       break;\r
4942 \r
4943     case IDM_EditServers:\r
4944       EditTagsPopUp(icsNames, &icsNames);\r
4945       break;\r
4946 \r
4947     case IDM_EditTags:\r
4948     case IDM_Tags:\r
4949       EditTagsProc();\r
4950       break;\r
4951 \r
4952     case IDM_EditComment:\r
4953     case IDM_Comment:\r
4954       if (commentUp && editComment) {\r
4955         CommentPopDown();\r
4956       } else {\r
4957         EditCommentEvent();\r
4958       }\r
4959       break;\r
4960 \r
4961     case IDM_Pause:\r
4962       PauseEvent();\r
4963       break;\r
4964 \r
4965     case IDM_Accept:\r
4966       AcceptEvent();\r
4967       break;\r
4968 \r
4969     case IDM_Decline:\r
4970       DeclineEvent();\r
4971       break;\r
4972 \r
4973     case IDM_Rematch:\r
4974       RematchEvent();\r
4975       break;\r
4976 \r
4977     case IDM_CallFlag:\r
4978       CallFlagEvent();\r
4979       break;\r
4980 \r
4981     case IDM_Draw:\r
4982       DrawEvent();\r
4983       break;\r
4984 \r
4985     case IDM_Adjourn:\r
4986       AdjournEvent();\r
4987       break;\r
4988 \r
4989     case IDM_Abort:\r
4990       AbortEvent();\r
4991       break;\r
4992 \r
4993     case IDM_Resign:\r
4994       ResignEvent();\r
4995       break;\r
4996 \r
4997     case IDM_StopObserving:\r
4998       StopObservingEvent();\r
4999       break;\r
5000 \r
5001     case IDM_StopExamining:\r
5002       StopExaminingEvent();\r
5003       break;\r
5004 \r
5005     case IDM_Upload:\r
5006       UploadGameEvent();\r
5007       break;\r
5008 \r
5009     case IDM_TypeInMove:\r
5010       PopUpMoveDialog('\000');\r
5011       break;\r
5012 \r
5013     case IDM_TypeInName:\r
5014       PopUpNameDialog('\000');\r
5015       break;\r
5016 \r
5017     case IDM_Backward:\r
5018       BackwardEvent();\r
5019       SetFocus(hwndMain);\r
5020       break;\r
5021 \r
5022     JAWS_MENU_ITEMS\r
5023 \r
5024     case IDM_Forward:\r
5025       ForwardEvent();\r
5026       SetFocus(hwndMain);\r
5027       break;\r
5028 \r
5029     case IDM_ToStart:\r
5030       ToStartEvent();\r
5031       SetFocus(hwndMain);\r
5032       break;\r
5033 \r
5034     case IDM_ToEnd:\r
5035       ToEndEvent();\r
5036       SetFocus(hwndMain);\r
5037       break;\r
5038 \r
5039     case IDM_Revert:\r
5040       RevertEvent(FALSE);\r
5041       break;\r
5042 \r
5043     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5044       RevertEvent(TRUE);\r
5045       break;\r
5046 \r
5047     case IDM_TruncateGame:\r
5048       TruncateGameEvent();\r
5049       break;\r
5050 \r
5051     case IDM_MoveNow:\r
5052       MoveNowEvent();\r
5053       break;\r
5054 \r
5055     case IDM_RetractMove:\r
5056       RetractMoveEvent();\r
5057       break;\r
5058 \r
5059     case IDM_FlipView:\r
5060       flipView = !flipView;\r
5061       DrawPosition(FALSE, NULL);\r
5062       break;\r
5063 \r
5064     case IDM_FlipClock:\r
5065       flipClock = !flipClock;\r
5066       DisplayBothClocks();\r
5067       DrawPosition(FALSE, NULL);\r
5068       break;\r
5069 \r
5070     case IDM_MuteSounds:\r
5071       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5072       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5073                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5074       break;\r
5075 \r
5076     case IDM_GeneralOptions:\r
5077       GeneralOptionsPopup(hwnd);\r
5078       DrawPosition(TRUE, NULL);\r
5079       break;\r
5080 \r
5081     case IDM_BoardOptions:\r
5082       BoardOptionsPopup(hwnd);\r
5083       break;\r
5084 \r
5085     case IDM_EnginePlayOptions:\r
5086       EnginePlayOptionsPopup(hwnd);\r
5087       break;\r
5088 \r
5089     case IDM_Engine1Options:\r
5090       EngineOptionsPopup(hwnd, &first);\r
5091       break;\r
5092 \r
5093     case IDM_Engine2Options:\r
5094       savedHwnd = hwnd;\r
5095       if(WaitForSecond(SettingsMenuIfReady)) break;\r
5096       EngineOptionsPopup(hwnd, &second);\r
5097       break;\r
5098 \r
5099     case IDM_OptionsUCI:\r
5100       UciOptionsPopup(hwnd);\r
5101       break;\r
5102 \r
5103     case IDM_IcsOptions:\r
5104       IcsOptionsPopup(hwnd);\r
5105       break;\r
5106 \r
5107     case IDM_Fonts:\r
5108       FontsOptionsPopup(hwnd);\r
5109       break;\r
5110 \r
5111     case IDM_Sounds:\r
5112       SoundOptionsPopup(hwnd);\r
5113       break;\r
5114 \r
5115     case IDM_CommPort:\r
5116       CommPortOptionsPopup(hwnd);\r
5117       break;\r
5118 \r
5119     case IDM_LoadOptions:\r
5120       LoadOptionsPopup(hwnd);\r
5121       break;\r
5122 \r
5123     case IDM_SaveOptions:\r
5124       SaveOptionsPopup(hwnd);\r
5125       break;\r
5126 \r
5127     case IDM_TimeControl:\r
5128       TimeControlOptionsPopup(hwnd);\r
5129       break;\r
5130 \r
5131     case IDM_SaveSettings:\r
5132       SaveSettings(settingsFileName);\r
5133       break;\r
5134 \r
5135     case IDM_SaveSettingsOnExit:\r
5136       saveSettingsOnExit = !saveSettingsOnExit;\r
5137       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5138                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5139                                          MF_CHECKED : MF_UNCHECKED));\r
5140       break;\r
5141 \r
5142     case IDM_Hint:\r
5143       HintEvent();\r
5144       break;\r
5145 \r
5146     case IDM_Book:\r
5147       BookEvent();\r
5148       break;\r
5149 \r
5150     case IDM_AboutGame:\r
5151       AboutGameEvent();\r
5152       break;\r
5153 \r
5154     case IDM_Debug:\r
5155       appData.debugMode = !appData.debugMode;\r
5156       if (appData.debugMode) {\r
5157         char dir[MSG_SIZ];\r
5158         GetCurrentDirectory(MSG_SIZ, dir);\r
5159         SetCurrentDirectory(installDir);\r
5160         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5161         SetCurrentDirectory(dir);\r
5162         setbuf(debugFP, NULL);\r
5163       } else {\r
5164         fclose(debugFP);\r
5165         debugFP = NULL;\r
5166       }\r
5167       break;\r
5168 \r
5169     case IDM_HELPCONTENTS:\r
5170       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5171           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5172           MessageBox (GetFocus(),\r
5173                     _("Unable to activate help"),\r
5174                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5175       }\r
5176       break;\r
5177 \r
5178     case IDM_HELPSEARCH:\r
5179         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5180             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5181         MessageBox (GetFocus(),\r
5182                     _("Unable to activate help"),\r
5183                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5184       }\r
5185       break;\r
5186 \r
5187     case IDM_HELPHELP:\r
5188       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5189         MessageBox (GetFocus(),\r
5190                     _("Unable to activate help"),\r
5191                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5192       }\r
5193       break;\r
5194 \r
5195     case IDM_ABOUT:\r
5196       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5197       DialogBox(hInst, \r
5198         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5199         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5200       FreeProcInstance(lpProc);\r
5201       break;\r
5202 \r
5203     case IDM_DirectCommand1:\r
5204       AskQuestionEvent(_("Direct Command"),\r
5205                        _("Send to chess program:"), "", "1");\r
5206       break;\r
5207     case IDM_DirectCommand2:\r
5208       AskQuestionEvent(_("Direct Command"),\r
5209                        _("Send to second chess program:"), "", "2");\r
5210       break;\r
5211 \r
5212     case EP_WhitePawn:\r
5213       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5214       fromX = fromY = -1;\r
5215       break;\r
5216 \r
5217     case EP_WhiteKnight:\r
5218       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5219       fromX = fromY = -1;\r
5220       break;\r
5221 \r
5222     case EP_WhiteBishop:\r
5223       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5224       fromX = fromY = -1;\r
5225       break;\r
5226 \r
5227     case EP_WhiteRook:\r
5228       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5229       fromX = fromY = -1;\r
5230       break;\r
5231 \r
5232     case EP_WhiteQueen:\r
5233       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5234       fromX = fromY = -1;\r
5235       break;\r
5236 \r
5237     case EP_WhiteFerz:\r
5238       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5239       fromX = fromY = -1;\r
5240       break;\r
5241 \r
5242     case EP_WhiteWazir:\r
5243       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5244       fromX = fromY = -1;\r
5245       break;\r
5246 \r
5247     case EP_WhiteAlfil:\r
5248       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5249       fromX = fromY = -1;\r
5250       break;\r
5251 \r
5252     case EP_WhiteCannon:\r
5253       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5254       fromX = fromY = -1;\r
5255       break;\r
5256 \r
5257     case EP_WhiteCardinal:\r
5258       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5259       fromX = fromY = -1;\r
5260       break;\r
5261 \r
5262     case EP_WhiteMarshall:\r
5263       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5264       fromX = fromY = -1;\r
5265       break;\r
5266 \r
5267     case EP_WhiteKing:\r
5268       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5269       fromX = fromY = -1;\r
5270       break;\r
5271 \r
5272     case EP_BlackPawn:\r
5273       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5274       fromX = fromY = -1;\r
5275       break;\r
5276 \r
5277     case EP_BlackKnight:\r
5278       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5279       fromX = fromY = -1;\r
5280       break;\r
5281 \r
5282     case EP_BlackBishop:\r
5283       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5284       fromX = fromY = -1;\r
5285       break;\r
5286 \r
5287     case EP_BlackRook:\r
5288       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5289       fromX = fromY = -1;\r
5290       break;\r
5291 \r
5292     case EP_BlackQueen:\r
5293       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5294       fromX = fromY = -1;\r
5295       break;\r
5296 \r
5297     case EP_BlackFerz:\r
5298       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5299       fromX = fromY = -1;\r
5300       break;\r
5301 \r
5302     case EP_BlackWazir:\r
5303       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5304       fromX = fromY = -1;\r
5305       break;\r
5306 \r
5307     case EP_BlackAlfil:\r
5308       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5309       fromX = fromY = -1;\r
5310       break;\r
5311 \r
5312     case EP_BlackCannon:\r
5313       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5314       fromX = fromY = -1;\r
5315       break;\r
5316 \r
5317     case EP_BlackCardinal:\r
5318       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5319       fromX = fromY = -1;\r
5320       break;\r
5321 \r
5322     case EP_BlackMarshall:\r
5323       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5324       fromX = fromY = -1;\r
5325       break;\r
5326 \r
5327     case EP_BlackKing:\r
5328       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5329       fromX = fromY = -1;\r
5330       break;\r
5331 \r
5332     case EP_EmptySquare:\r
5333       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5334       fromX = fromY = -1;\r
5335       break;\r
5336 \r
5337     case EP_ClearBoard:\r
5338       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5339       fromX = fromY = -1;\r
5340       break;\r
5341 \r
5342     case EP_White:\r
5343       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5344       fromX = fromY = -1;\r
5345       break;\r
5346 \r
5347     case EP_Black:\r
5348       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5349       fromX = fromY = -1;\r
5350       break;\r
5351 \r
5352     case EP_Promote:\r
5353       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5354       fromX = fromY = -1;\r
5355       break;\r
5356 \r
5357     case EP_Demote:\r
5358       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5359       fromX = fromY = -1;\r
5360       break;\r
5361 \r
5362     case DP_Pawn:\r
5363       DropMenuEvent(WhitePawn, fromX, fromY);\r
5364       fromX = fromY = -1;\r
5365       break;\r
5366 \r
5367     case DP_Knight:\r
5368       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5369       fromX = fromY = -1;\r
5370       break;\r
5371 \r
5372     case DP_Bishop:\r
5373       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5374       fromX = fromY = -1;\r
5375       break;\r
5376 \r
5377     case DP_Rook:\r
5378       DropMenuEvent(WhiteRook, fromX, fromY);\r
5379       fromX = fromY = -1;\r
5380       break;\r
5381 \r
5382     case DP_Queen:\r
5383       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5384       fromX = fromY = -1;\r
5385       break;\r
5386 \r
5387     case IDM_English:\r
5388       barbaric = 0;\r
5389       TranslateMenus(0);\r
5390       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5391       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5392       lastChecked = wmId;\r
5393       break;\r
5394 \r
5395     default:\r
5396       if(wmId > IDM_English && wmId < IDM_English+5) {\r
5397           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5398           TranslateMenus(0);\r
5399           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5400           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5401           lastChecked = wmId;\r
5402           break;\r
5403       }\r
5404       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5405     }\r
5406     break;\r
5407 \r
5408   case WM_TIMER:\r
5409     switch (wParam) {\r
5410     case CLOCK_TIMER_ID:\r
5411       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5412       clockTimerEvent = 0;\r
5413       DecrementClocks(); /* call into back end */\r
5414       break;\r
5415     case LOAD_GAME_TIMER_ID:\r
5416       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5417       loadGameTimerEvent = 0;\r
5418       AutoPlayGameLoop(); /* call into back end */\r
5419       break;\r
5420     case ANALYSIS_TIMER_ID:\r
5421       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5422                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5423         AnalysisPeriodicEvent(0);\r
5424       } else {\r
5425         KillTimer(hwnd, analysisTimerEvent);\r
5426         analysisTimerEvent = 0;\r
5427       }\r
5428       break;\r
5429     case DELAYED_TIMER_ID:\r
5430       KillTimer(hwnd, delayedTimerEvent);\r
5431       delayedTimerEvent = 0;\r
5432       delayedTimerCallback();\r
5433       break;\r
5434     }\r
5435     break;\r
5436 \r
5437   case WM_USER_Input:\r
5438     InputEvent(hwnd, message, wParam, lParam);\r
5439     break;\r
5440 \r
5441   /* [AS] Also move "attached" child windows */\r
5442   case WM_WINDOWPOSCHANGING:\r
5443 \r
5444     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5445         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5446 \r
5447         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5448             /* Window is moving */\r
5449             RECT rcMain;\r
5450 \r
5451 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5452             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5453             rcMain.right  = wpMain.x + wpMain.width;\r
5454             rcMain.top    = wpMain.y;\r
5455             rcMain.bottom = wpMain.y + wpMain.height;\r
5456             \r
5457             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5458             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5459             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5460             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5461             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5462             wpMain.x = lpwp->x;\r
5463             wpMain.y = lpwp->y;\r
5464         }\r
5465     }\r
5466     break;\r
5467 \r
5468   /* [AS] Snapping */\r
5469   case WM_ENTERSIZEMOVE:\r
5470     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5471     if (hwnd == hwndMain) {\r
5472       doingSizing = TRUE;\r
5473       lastSizing = 0;\r
5474     }\r
5475     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5476     break;\r
5477 \r
5478   case WM_SIZING:\r
5479     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5480     if (hwnd == hwndMain) {\r
5481       lastSizing = wParam;\r
5482     }\r
5483     break;\r
5484 \r
5485   case WM_MOVING:\r
5486     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5487       return OnMoving( &sd, hwnd, wParam, lParam );\r
5488 \r
5489   case WM_EXITSIZEMOVE:\r
5490     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5491     if (hwnd == hwndMain) {\r
5492       RECT client;\r
5493       doingSizing = FALSE;\r
5494       InvalidateRect(hwnd, &boardRect, FALSE);\r
5495       GetClientRect(hwnd, &client);\r
5496       ResizeBoard(client.right, client.bottom, lastSizing);\r
5497       lastSizing = 0;\r
5498       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5499     }\r
5500     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5501     break;\r
5502 \r
5503   case WM_DESTROY: /* message: window being destroyed */\r
5504     PostQuitMessage(0);\r
5505     break;\r
5506 \r
5507   case WM_CLOSE:\r
5508     if (hwnd == hwndMain) {\r
5509       ExitEvent(0);\r
5510     }\r
5511     break;\r
5512 \r
5513   default:      /* Passes it on if unprocessed */\r
5514     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5515   }\r
5516   return 0;\r
5517 }\r
5518 \r
5519 /*---------------------------------------------------------------------------*\\r
5520  *\r
5521  * Misc utility routines\r
5522  *\r
5523 \*---------------------------------------------------------------------------*/\r
5524 \r
5525 /*\r
5526  * Decent random number generator, at least not as bad as Windows\r
5527  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5528  */\r
5529 unsigned int randstate;\r
5530 \r
5531 int\r
5532 myrandom(void)\r
5533 {\r
5534   randstate = randstate * 1664525 + 1013904223;\r
5535   return (int) randstate & 0x7fffffff;\r
5536 }\r
5537 \r
5538 void\r
5539 mysrandom(unsigned int seed)\r
5540 {\r
5541   randstate = seed;\r
5542 }\r
5543 \r
5544 \r
5545 /* \r
5546  * returns TRUE if user selects a different color, FALSE otherwise \r
5547  */\r
5548 \r
5549 BOOL\r
5550 ChangeColor(HWND hwnd, COLORREF *which)\r
5551 {\r
5552   static BOOL firstTime = TRUE;\r
5553   static DWORD customColors[16];\r
5554   CHOOSECOLOR cc;\r
5555   COLORREF newcolor;\r
5556   int i;\r
5557   ColorClass ccl;\r
5558 \r
5559   if (firstTime) {\r
5560     /* Make initial colors in use available as custom colors */\r
5561     /* Should we put the compiled-in defaults here instead? */\r
5562     i = 0;\r
5563     customColors[i++] = lightSquareColor & 0xffffff;\r
5564     customColors[i++] = darkSquareColor & 0xffffff;\r
5565     customColors[i++] = whitePieceColor & 0xffffff;\r
5566     customColors[i++] = blackPieceColor & 0xffffff;\r
5567     customColors[i++] = highlightSquareColor & 0xffffff;\r
5568     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5569 \r
5570     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5571       customColors[i++] = textAttribs[ccl].color;\r
5572     }\r
5573     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5574     firstTime = FALSE;\r
5575   }\r
5576 \r
5577   cc.lStructSize = sizeof(cc);\r
5578   cc.hwndOwner = hwnd;\r
5579   cc.hInstance = NULL;\r
5580   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5581   cc.lpCustColors = (LPDWORD) customColors;\r
5582   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5583 \r
5584   if (!ChooseColor(&cc)) return FALSE;\r
5585 \r
5586   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5587   if (newcolor == *which) return FALSE;\r
5588   *which = newcolor;\r
5589   return TRUE;\r
5590 \r
5591   /*\r
5592   InitDrawingColors();\r
5593   InvalidateRect(hwnd, &boardRect, FALSE);\r
5594   */\r
5595 }\r
5596 \r
5597 BOOLEAN\r
5598 MyLoadSound(MySound *ms)\r
5599 {\r
5600   BOOL ok = FALSE;\r
5601   struct stat st;\r
5602   FILE *f;\r
5603 \r
5604   if (ms->data) free(ms->data);\r
5605   ms->data = NULL;\r
5606 \r
5607   switch (ms->name[0]) {\r
5608   case NULLCHAR:\r
5609     /* Silence */\r
5610     ok = TRUE;\r
5611     break;\r
5612   case '$':\r
5613     /* System sound from Control Panel.  Don't preload here. */\r
5614     ok = TRUE;\r
5615     break;\r
5616   case '!':\r
5617     if (ms->name[1] == NULLCHAR) {\r
5618       /* "!" alone = silence */\r
5619       ok = TRUE;\r
5620     } else {\r
5621       /* Builtin wave resource.  Error if not found. */\r
5622       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5623       if (h == NULL) break;\r
5624       ms->data = (void *)LoadResource(hInst, h);\r
5625       if (h == NULL) break;\r
5626       ok = TRUE;\r
5627     }\r
5628     break;\r
5629   default:\r
5630     /* .wav file.  Error if not found. */\r
5631     f = fopen(ms->name, "rb");\r
5632     if (f == NULL) break;\r
5633     if (fstat(fileno(f), &st) < 0) break;\r
5634     ms->data = malloc(st.st_size);\r
5635     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5636     fclose(f);\r
5637     ok = TRUE;\r
5638     break;\r
5639   }\r
5640   if (!ok) {\r
5641     char buf[MSG_SIZ];\r
5642       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5643     DisplayError(buf, GetLastError());\r
5644   }\r
5645   return ok;\r
5646 }\r
5647 \r
5648 BOOLEAN\r
5649 MyPlaySound(MySound *ms)\r
5650 {\r
5651   BOOLEAN ok = FALSE;\r
5652 \r
5653   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5654   switch (ms->name[0]) {\r
5655   case NULLCHAR:\r
5656         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5657     /* Silence */\r
5658     ok = TRUE;\r
5659     break;\r
5660   case '$':\r
5661     /* System sound from Control Panel (deprecated feature).\r
5662        "$" alone or an unset sound name gets default beep (still in use). */\r
5663     if (ms->name[1]) {\r
5664       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5665     }\r
5666     if (!ok) ok = MessageBeep(MB_OK);\r
5667     break; \r
5668   case '!':\r
5669     /* Builtin wave resource, or "!" alone for silence */\r
5670     if (ms->name[1]) {\r
5671       if (ms->data == NULL) return FALSE;\r
5672       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5673     } else {\r
5674       ok = TRUE;\r
5675     }\r
5676     break;\r
5677   default:\r
5678     /* .wav file.  Error if not found. */\r
5679     if (ms->data == NULL) return FALSE;\r
5680     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5681     break;\r
5682   }\r
5683   /* Don't print an error: this can happen innocently if the sound driver\r
5684      is busy; for instance, if another instance of WinBoard is playing\r
5685      a sound at about the same time. */\r
5686   return ok;\r
5687 }\r
5688 \r
5689 \r
5690 LRESULT CALLBACK\r
5691 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5692 {\r
5693   BOOL ok;\r
5694   OPENFILENAME *ofn;\r
5695   static UINT *number; /* gross that this is static */\r
5696 \r
5697   switch (message) {\r
5698   case WM_INITDIALOG: /* message: initialize dialog box */\r
5699     /* Center the dialog over the application window */\r
5700     ofn = (OPENFILENAME *) lParam;\r
5701     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5702       number = (UINT *) ofn->lCustData;\r
5703       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5704     } else {\r
5705       number = NULL;\r
5706     }\r
5707     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5708     Translate(hDlg, 1536);\r
5709     return FALSE;  /* Allow for further processing */\r
5710 \r
5711   case WM_COMMAND:\r
5712     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5713       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5714     }\r
5715     return FALSE;  /* Allow for further processing */\r
5716   }\r
5717   return FALSE;\r
5718 }\r
5719 \r
5720 UINT APIENTRY\r
5721 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5722 {\r
5723   static UINT *number;\r
5724   OPENFILENAME *ofname;\r
5725   OFNOTIFY *ofnot;\r
5726   switch (uiMsg) {\r
5727   case WM_INITDIALOG:\r
5728     Translate(hdlg, DLG_IndexNumber);\r
5729     ofname = (OPENFILENAME *)lParam;\r
5730     number = (UINT *)(ofname->lCustData);\r
5731     break;\r
5732   case WM_NOTIFY:\r
5733     ofnot = (OFNOTIFY *)lParam;\r
5734     if (ofnot->hdr.code == CDN_FILEOK) {\r
5735       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5736     }\r
5737     break;\r
5738   }\r
5739   return 0;\r
5740 }\r
5741 \r
5742 \r
5743 FILE *\r
5744 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5745                char *nameFilt, char *dlgTitle, UINT *number,\r
5746                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5747 {\r
5748   OPENFILENAME openFileName;\r
5749   char buf1[MSG_SIZ];\r
5750   FILE *f;\r
5751 \r
5752   if (fileName == NULL) fileName = buf1;\r
5753   if (defName == NULL) {\r
5754     safeStrCpy(fileName, "*.", 3 );\r
5755     strcat(fileName, defExt);\r
5756   } else {\r
5757     safeStrCpy(fileName, defName, MSG_SIZ );\r
5758   }\r
5759     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5760   if (number) *number = 0;\r
5761 \r
5762   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5763   openFileName.hwndOwner         = hwnd;\r
5764   openFileName.hInstance         = (HANDLE) hInst;\r
5765   openFileName.lpstrFilter       = nameFilt;\r
5766   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5767   openFileName.nMaxCustFilter    = 0L;\r
5768   openFileName.nFilterIndex      = 1L;\r
5769   openFileName.lpstrFile         = fileName;\r
5770   openFileName.nMaxFile          = MSG_SIZ;\r
5771   openFileName.lpstrFileTitle    = fileTitle;\r
5772   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5773   openFileName.lpstrInitialDir   = NULL;\r
5774   openFileName.lpstrTitle        = dlgTitle;\r
5775   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5776     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5777     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5778     | (oldDialog ? 0 : OFN_EXPLORER);\r
5779   openFileName.nFileOffset       = 0;\r
5780   openFileName.nFileExtension    = 0;\r
5781   openFileName.lpstrDefExt       = defExt;\r
5782   openFileName.lCustData         = (LONG) number;\r
5783   openFileName.lpfnHook          = oldDialog ?\r
5784     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5785   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5786 \r
5787   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5788                         GetOpenFileName(&openFileName)) {\r
5789     /* open the file */\r
5790     f = fopen(openFileName.lpstrFile, write);\r
5791     if (f == NULL) {\r
5792       MessageBox(hwnd, _("File open failed"), NULL,\r
5793                  MB_OK|MB_ICONEXCLAMATION);\r
5794       return NULL;\r
5795     }\r
5796   } else {\r
5797     int err = CommDlgExtendedError();\r
5798     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5799     return FALSE;\r
5800   }\r
5801   return f;\r
5802 }\r
5803 \r
5804 \r
5805 \r
5806 VOID APIENTRY\r
5807 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5808 {\r
5809   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5810 \r
5811   /*\r
5812    * Get the first pop-up menu in the menu template. This is the\r
5813    * menu that TrackPopupMenu displays.\r
5814    */\r
5815   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5816   TranslateOneMenu(10, hmenuTrackPopup);\r
5817 \r
5818   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5819 \r
5820   /*\r
5821    * TrackPopup uses screen coordinates, so convert the\r
5822    * coordinates of the mouse click to screen coordinates.\r
5823    */\r
5824   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5825 \r
5826   /* Draw and track the floating pop-up menu. */\r
5827   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5828                  pt.x, pt.y, 0, hwnd, NULL);\r
5829 \r
5830   /* Destroy the menu.*/\r
5831   DestroyMenu(hmenu);\r
5832 }\r
5833    \r
5834 typedef struct {\r
5835   HWND hDlg, hText;\r
5836   int sizeX, sizeY, newSizeX, newSizeY;\r
5837   HDWP hdwp;\r
5838 } ResizeEditPlusButtonsClosure;\r
5839 \r
5840 BOOL CALLBACK\r
5841 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5842 {\r
5843   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5844   RECT rect;\r
5845   POINT pt;\r
5846 \r
5847   if (hChild == cl->hText) return TRUE;\r
5848   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5849   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5850   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5851   ScreenToClient(cl->hDlg, &pt);\r
5852   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5853     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5854   return TRUE;\r
5855 }\r
5856 \r
5857 /* Resize a dialog that has a (rich) edit field filling most of\r
5858    the top, with a row of buttons below */\r
5859 VOID\r
5860 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5861 {\r
5862   RECT rectText;\r
5863   int newTextHeight, newTextWidth;\r
5864   ResizeEditPlusButtonsClosure cl;\r
5865   \r
5866   /*if (IsIconic(hDlg)) return;*/\r
5867   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5868   \r
5869   cl.hdwp = BeginDeferWindowPos(8);\r
5870 \r
5871   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5872   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5873   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5874   if (newTextHeight < 0) {\r
5875     newSizeY += -newTextHeight;\r
5876     newTextHeight = 0;\r
5877   }\r
5878   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5879     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5880 \r
5881   cl.hDlg = hDlg;\r
5882   cl.hText = hText;\r
5883   cl.sizeX = sizeX;\r
5884   cl.sizeY = sizeY;\r
5885   cl.newSizeX = newSizeX;\r
5886   cl.newSizeY = newSizeY;\r
5887   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5888 \r
5889   EndDeferWindowPos(cl.hdwp);\r
5890 }\r
5891 \r
5892 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5893 {\r
5894     RECT    rChild, rParent;\r
5895     int     wChild, hChild, wParent, hParent;\r
5896     int     wScreen, hScreen, xNew, yNew;\r
5897     HDC     hdc;\r
5898 \r
5899     /* Get the Height and Width of the child window */\r
5900     GetWindowRect (hwndChild, &rChild);\r
5901     wChild = rChild.right - rChild.left;\r
5902     hChild = rChild.bottom - rChild.top;\r
5903 \r
5904     /* Get the Height and Width of the parent window */\r
5905     GetWindowRect (hwndParent, &rParent);\r
5906     wParent = rParent.right - rParent.left;\r
5907     hParent = rParent.bottom - rParent.top;\r
5908 \r
5909     /* Get the display limits */\r
5910     hdc = GetDC (hwndChild);\r
5911     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5912     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5913     ReleaseDC(hwndChild, hdc);\r
5914 \r
5915     /* Calculate new X position, then adjust for screen */\r
5916     xNew = rParent.left + ((wParent - wChild) /2);\r
5917     if (xNew < 0) {\r
5918         xNew = 0;\r
5919     } else if ((xNew+wChild) > wScreen) {\r
5920         xNew = wScreen - wChild;\r
5921     }\r
5922 \r
5923     /* Calculate new Y position, then adjust for screen */\r
5924     if( mode == 0 ) {\r
5925         yNew = rParent.top  + ((hParent - hChild) /2);\r
5926     }\r
5927     else {\r
5928         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5929     }\r
5930 \r
5931     if (yNew < 0) {\r
5932         yNew = 0;\r
5933     } else if ((yNew+hChild) > hScreen) {\r
5934         yNew = hScreen - hChild;\r
5935     }\r
5936 \r
5937     /* Set it, and return */\r
5938     return SetWindowPos (hwndChild, NULL,\r
5939                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5940 }\r
5941 \r
5942 /* Center one window over another */\r
5943 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
5944 {\r
5945     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
5946 }\r
5947 \r
5948 /*---------------------------------------------------------------------------*\\r
5949  *\r
5950  * Startup Dialog functions\r
5951  *\r
5952 \*---------------------------------------------------------------------------*/\r
5953 void\r
5954 InitComboStrings(HANDLE hwndCombo, char **cd)\r
5955 {\r
5956   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5957 \r
5958   while (*cd != NULL) {\r
5959     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
5960     cd++;\r
5961   }\r
5962 }\r
5963 \r
5964 void\r
5965 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
5966 {\r
5967   char buf1[MAX_ARG_LEN];\r
5968   int len;\r
5969 \r
5970   if (str[0] == '@') {\r
5971     FILE* f = fopen(str + 1, "r");\r
5972     if (f == NULL) {\r
5973       DisplayFatalError(str + 1, errno, 2);\r
5974       return;\r
5975     }\r
5976     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
5977     fclose(f);\r
5978     buf1[len] = NULLCHAR;\r
5979     str = buf1;\r
5980   }\r
5981 \r
5982   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5983 \r
5984   for (;;) {\r
5985     char buf[MSG_SIZ];\r
5986     char *end = strchr(str, '\n');\r
5987     if (end == NULL) return;\r
5988     memcpy(buf, str, end - str);\r
5989     buf[end - str] = NULLCHAR;\r
5990     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
5991     str = end + 1;\r
5992   }\r
5993 }\r
5994 \r
5995 void\r
5996 SetStartupDialogEnables(HWND hDlg)\r
5997 {\r
5998   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
5999     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6000     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6001   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6002     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6003   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6004     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6005   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6006     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6007   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6008     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6009     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6010     IsDlgButtonChecked(hDlg, OPT_View));\r
6011 }\r
6012 \r
6013 char *\r
6014 QuoteForFilename(char *filename)\r
6015 {\r
6016   int dquote, space;\r
6017   dquote = strchr(filename, '"') != NULL;\r
6018   space = strchr(filename, ' ') != NULL;\r
6019   if (dquote || space) {\r
6020     if (dquote) {\r
6021       return "'";\r
6022     } else {\r
6023       return "\"";\r
6024     }\r
6025   } else {\r
6026     return "";\r
6027   }\r
6028 }\r
6029 \r
6030 VOID\r
6031 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6032 {\r
6033   char buf[MSG_SIZ];\r
6034   char *q;\r
6035 \r
6036   InitComboStringsFromOption(hwndCombo, nthnames);\r
6037   q = QuoteForFilename(nthcp);\r
6038     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6039   if (*nthdir != NULLCHAR) {\r
6040     q = QuoteForFilename(nthdir);\r
6041       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6042   }\r
6043   if (*nthcp == NULLCHAR) {\r
6044     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6045   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6046     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6047     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6048   }\r
6049 }\r
6050 \r
6051 LRESULT CALLBACK\r
6052 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6053 {\r
6054   char buf[MSG_SIZ];\r
6055   HANDLE hwndCombo;\r
6056   char *p;\r
6057 \r
6058   switch (message) {\r
6059   case WM_INITDIALOG:\r
6060     /* Center the dialog */\r
6061     CenterWindow (hDlg, GetDesktopWindow());\r
6062     Translate(hDlg, DLG_Startup);\r
6063     /* Initialize the dialog items */\r
6064     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6065                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6066                   firstChessProgramNames);\r
6067     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6068                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6069                   secondChessProgramNames);\r
6070     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6071     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6072       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6073     if (*appData.icsHelper != NULLCHAR) {\r
6074       char *q = QuoteForFilename(appData.icsHelper);\r
6075       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6076     }\r
6077     if (*appData.icsHost == NULLCHAR) {\r
6078       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6079       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6080     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6081       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6082       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6083     }\r
6084 \r
6085     if (appData.icsActive) {\r
6086       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6087     }\r
6088     else if (appData.noChessProgram) {\r
6089       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6090     }\r
6091     else {\r
6092       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6093     }\r
6094 \r
6095     SetStartupDialogEnables(hDlg);\r
6096     return TRUE;\r
6097 \r
6098   case WM_COMMAND:\r
6099     switch (LOWORD(wParam)) {\r
6100     case IDOK:\r
6101       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6102         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6103         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6104         p = buf;\r
6105         ParseArgs(StringGet, &p);\r
6106         safeStrCpy(buf, "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6107         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6108         p = buf;\r
6109         ParseArgs(StringGet, &p);\r
6110         appData.noChessProgram = FALSE;\r
6111         appData.icsActive = FALSE;\r
6112       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6113         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6114         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6115         p = buf;\r
6116         ParseArgs(StringGet, &p);\r
6117         if (appData.zippyPlay) {\r
6118           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6119           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6120           p = buf;\r
6121           ParseArgs(StringGet, &p);\r
6122         }\r
6123       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6124         appData.noChessProgram = TRUE;\r
6125         appData.icsActive = FALSE;\r
6126       } else {\r
6127         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6128                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6129         return TRUE;\r
6130       }\r
6131       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6132         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6133         p = buf;\r
6134         ParseArgs(StringGet, &p);\r
6135       }\r
6136       EndDialog(hDlg, TRUE);\r
6137       return TRUE;\r
6138 \r
6139     case IDCANCEL:\r
6140       ExitEvent(0);\r
6141       return TRUE;\r
6142 \r
6143     case IDM_HELPCONTENTS:\r
6144       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6145         MessageBox (GetFocus(),\r
6146                     _("Unable to activate help"),\r
6147                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6148       }\r
6149       break;\r
6150 \r
6151     default:\r
6152       SetStartupDialogEnables(hDlg);\r
6153       break;\r
6154     }\r
6155     break;\r
6156   }\r
6157   return FALSE;\r
6158 }\r
6159 \r
6160 /*---------------------------------------------------------------------------*\\r
6161  *\r
6162  * About box dialog functions\r
6163  *\r
6164 \*---------------------------------------------------------------------------*/\r
6165 \r
6166 /* Process messages for "About" dialog box */\r
6167 LRESULT CALLBACK\r
6168 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6169 {\r
6170   switch (message) {\r
6171   case WM_INITDIALOG: /* message: initialize dialog box */\r
6172     /* Center the dialog over the application window */\r
6173     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6174     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6175     Translate(hDlg, ABOUTBOX);\r
6176     JAWS_COPYRIGHT\r
6177     return (TRUE);\r
6178 \r
6179   case WM_COMMAND: /* message: received a command */\r
6180     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6181         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6182       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6183       return (TRUE);\r
6184     }\r
6185     break;\r
6186   }\r
6187   return (FALSE);\r
6188 }\r
6189 \r
6190 /*---------------------------------------------------------------------------*\\r
6191  *\r
6192  * Comment Dialog functions\r
6193  *\r
6194 \*---------------------------------------------------------------------------*/\r
6195 \r
6196 LRESULT CALLBACK\r
6197 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6198 {\r
6199   static HANDLE hwndText = NULL;\r
6200   int len, newSizeX, newSizeY, flags;\r
6201   static int sizeX, sizeY;\r
6202   char *str;\r
6203   RECT rect;\r
6204   MINMAXINFO *mmi;\r
6205 \r
6206   switch (message) {\r
6207   case WM_INITDIALOG: /* message: initialize dialog box */\r
6208     /* Initialize the dialog items */\r
6209     Translate(hDlg, DLG_EditComment);\r
6210     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6211     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6212     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6213     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6214     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6215     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6216     SetWindowText(hDlg, commentTitle);\r
6217     if (editComment) {\r
6218       SetFocus(hwndText);\r
6219     } else {\r
6220       SetFocus(GetDlgItem(hDlg, IDOK));\r
6221     }\r
6222     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6223                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6224                 MAKELPARAM(FALSE, 0));\r
6225     /* Size and position the dialog */\r
6226     if (!commentDialog) {\r
6227       commentDialog = hDlg;\r
6228       flags = SWP_NOZORDER;\r
6229       GetClientRect(hDlg, &rect);\r
6230       sizeX = rect.right;\r
6231       sizeY = rect.bottom;\r
6232       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6233           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6234         WINDOWPLACEMENT wp;\r
6235         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6236         wp.length = sizeof(WINDOWPLACEMENT);\r
6237         wp.flags = 0;\r
6238         wp.showCmd = SW_SHOW;\r
6239         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6240         wp.rcNormalPosition.left = wpComment.x;\r
6241         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6242         wp.rcNormalPosition.top = wpComment.y;\r
6243         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6244         SetWindowPlacement(hDlg, &wp);\r
6245 \r
6246         GetClientRect(hDlg, &rect);\r
6247         newSizeX = rect.right;\r
6248         newSizeY = rect.bottom;\r
6249         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6250                               newSizeX, newSizeY);\r
6251         sizeX = newSizeX;\r
6252         sizeY = newSizeY;\r
6253       }\r
6254     }\r
6255     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6256     return FALSE;\r
6257 \r
6258   case WM_COMMAND: /* message: received a command */\r
6259     switch (LOWORD(wParam)) {\r
6260     case IDOK:\r
6261       if (editComment) {\r
6262         char *p, *q;\r
6263         /* Read changed options from the dialog box */\r
6264         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6265         len = GetWindowTextLength(hwndText);\r
6266         str = (char *) malloc(len + 1);\r
6267         GetWindowText(hwndText, str, len + 1);\r
6268         p = q = str;\r
6269         while (*q) {\r
6270           if (*q == '\r')\r
6271             q++;\r
6272           else\r
6273             *p++ = *q++;\r
6274         }\r
6275         *p = NULLCHAR;\r
6276         ReplaceComment(commentIndex, str);\r
6277         free(str);\r
6278       }\r
6279       CommentPopDown();\r
6280       return TRUE;\r
6281 \r
6282     case IDCANCEL:\r
6283     case OPT_CancelComment:\r
6284       CommentPopDown();\r
6285       return TRUE;\r
6286 \r
6287     case OPT_ClearComment:\r
6288       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6289       break;\r
6290 \r
6291     case OPT_EditComment:\r
6292       EditCommentEvent();\r
6293       return TRUE;\r
6294 \r
6295     default:\r
6296       break;\r
6297     }\r
6298     break;\r
6299 \r
6300   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6301         if( wParam == OPT_CommentText ) {\r
6302             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6303 \r
6304             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6305                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6306                 POINTL pt;\r
6307                 LRESULT index;\r
6308 \r
6309                 pt.x = LOWORD( lpMF->lParam );\r
6310                 pt.y = HIWORD( lpMF->lParam );\r
6311 \r
6312                 if(lpMF->msg == WM_CHAR) {\r
6313                         CHARRANGE sel;\r
6314                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6315                         index = sel.cpMin;\r
6316                 } else\r
6317                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6318 \r
6319                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6320                 len = GetWindowTextLength(hwndText);\r
6321                 str = (char *) malloc(len + 1);\r
6322                 GetWindowText(hwndText, str, len + 1);\r
6323                 ReplaceComment(commentIndex, str);\r
6324                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6325                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6326                 free(str);\r
6327 \r
6328                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6329                 lpMF->msg = WM_USER;\r
6330 \r
6331                 return TRUE;\r
6332             }\r
6333         }\r
6334         break;\r
6335 \r
6336   case WM_SIZE:\r
6337     newSizeX = LOWORD(lParam);\r
6338     newSizeY = HIWORD(lParam);\r
6339     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6340     sizeX = newSizeX;\r
6341     sizeY = newSizeY;\r
6342     break;\r
6343 \r
6344   case WM_GETMINMAXINFO:\r
6345     /* Prevent resizing window too small */\r
6346     mmi = (MINMAXINFO *) lParam;\r
6347     mmi->ptMinTrackSize.x = 100;\r
6348     mmi->ptMinTrackSize.y = 100;\r
6349     break;\r
6350   }\r
6351   return FALSE;\r
6352 }\r
6353 \r
6354 VOID\r
6355 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6356 {\r
6357   FARPROC lpProc;\r
6358   char *p, *q;\r
6359 \r
6360   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6361 \r
6362   if (str == NULL) str = "";\r
6363   p = (char *) malloc(2 * strlen(str) + 2);\r
6364   q = p;\r
6365   while (*str) {\r
6366     if (*str == '\n') *q++ = '\r';\r
6367     *q++ = *str++;\r
6368   }\r
6369   *q = NULLCHAR;\r
6370   if (commentText != NULL) free(commentText);\r
6371 \r
6372   commentIndex = index;\r
6373   commentTitle = title;\r
6374   commentText = p;\r
6375   editComment = edit;\r
6376 \r
6377   if (commentDialog) {\r
6378     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6379     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6380   } else {\r
6381     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6382     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6383                  hwndMain, (DLGPROC)lpProc);\r
6384     FreeProcInstance(lpProc);\r
6385   }\r
6386   commentUp = TRUE;\r
6387 }\r
6388 \r
6389 \r
6390 /*---------------------------------------------------------------------------*\\r
6391  *\r
6392  * Type-in move dialog functions\r
6393  * \r
6394 \*---------------------------------------------------------------------------*/\r
6395 \r
6396 LRESULT CALLBACK\r
6397 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6398 {\r
6399   char move[MSG_SIZ];\r
6400   HWND hInput;\r
6401   ChessMove moveType;\r
6402   int fromX, fromY, toX, toY;\r
6403   char promoChar;\r
6404 \r
6405   switch (message) {\r
6406   case WM_INITDIALOG:\r
6407     move[0] = (char) lParam;\r
6408     move[1] = NULLCHAR;\r
6409     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6410     Translate(hDlg, DLG_TypeInMove);\r
6411     hInput = GetDlgItem(hDlg, OPT_Move);\r
6412     SetWindowText(hInput, move);\r
6413     SetFocus(hInput);\r
6414     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6415     return FALSE;\r
6416 \r
6417   case WM_COMMAND:\r
6418     switch (LOWORD(wParam)) {\r
6419     case IDOK:
6420 \r
6421       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6422       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6423       { int n; Board board;\r
6424         // [HGM] FENedit\r
6425         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
6426                 EditPositionPasteFEN(move);\r
6427                 EndDialog(hDlg, TRUE);\r
6428                 return TRUE;\r
6429         }\r
6430         // [HGM] movenum: allow move number to be typed in any mode\r
6431         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
6432           ToNrEvent(2*n-1);\r
6433           EndDialog(hDlg, TRUE);\r
6434           return TRUE;\r
6435         }\r
6436       }\r
6437       if (gameMode != EditGame && currentMove != forwardMostMove && \r
6438         gameMode != Training) {\r
6439         DisplayMoveError(_("Displayed move is not current"));\r
6440       } else {\r
6441 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
6442         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6443           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
6444         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
6445         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6446           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
6447           if (gameMode != Training)\r
6448               forwardMostMove = currentMove;\r
6449           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
6450         } else {\r
6451           DisplayMoveError(_("Could not parse move"));\r
6452         }\r
6453       }\r
6454       EndDialog(hDlg, TRUE);\r
6455       return TRUE;\r
6456     case IDCANCEL:\r
6457       EndDialog(hDlg, FALSE);\r
6458       return TRUE;\r
6459     default:\r
6460       break;\r
6461     }\r
6462     break;\r
6463   }\r
6464   return FALSE;\r
6465 }\r
6466 \r
6467 VOID\r
6468 PopUpMoveDialog(char firstchar)\r
6469 {\r
6470     FARPROC lpProc;\r
6471     \r
6472     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
6473         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
6474         gameMode == AnalyzeMode || gameMode == EditGame || \r
6475         gameMode == EditPosition || gameMode == IcsExamining ||\r
6476         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
6477         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
6478                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
6479                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
6480         gameMode == Training) {\r
6481       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6482       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6483         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6484       FreeProcInstance(lpProc);\r
6485     }\r
6486 }\r
6487 \r
6488 /*---------------------------------------------------------------------------*\\r
6489  *\r
6490  * Type-in name dialog functions\r
6491  * \r
6492 \*---------------------------------------------------------------------------*/\r
6493 \r
6494 LRESULT CALLBACK\r
6495 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6496 {\r
6497   char move[MSG_SIZ];\r
6498   HWND hInput;\r
6499 \r
6500   switch (message) {\r
6501   case WM_INITDIALOG:\r
6502     move[0] = (char) lParam;\r
6503     move[1] = NULLCHAR;\r
6504     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6505     Translate(hDlg, DLG_TypeInName);\r
6506     hInput = GetDlgItem(hDlg, OPT_Name);\r
6507     SetWindowText(hInput, move);\r
6508     SetFocus(hInput);\r
6509     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6510     return FALSE;\r
6511 \r
6512   case WM_COMMAND:\r
6513     switch (LOWORD(wParam)) {\r
6514     case IDOK:\r
6515       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6516       appData.userName = strdup(move);\r
6517       SetUserLogo();\r
6518       SetGameInfo();\r
6519       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6520         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6521         DisplayTitle(move);\r
6522       }\r
6523 \r
6524 \r
6525       EndDialog(hDlg, TRUE);\r
6526       return TRUE;\r
6527     case IDCANCEL:\r
6528       EndDialog(hDlg, FALSE);\r
6529       return TRUE;\r
6530     default:\r
6531       break;\r
6532     }\r
6533     break;\r
6534   }\r
6535   return FALSE;\r
6536 }\r
6537 \r
6538 VOID\r
6539 PopUpNameDialog(char firstchar)\r
6540 {\r
6541     FARPROC lpProc;\r
6542     \r
6543       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6544       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6545         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6546       FreeProcInstance(lpProc);\r
6547 }\r
6548 \r
6549 /*---------------------------------------------------------------------------*\\r
6550  *\r
6551  *  Error dialogs\r
6552  * \r
6553 \*---------------------------------------------------------------------------*/\r
6554 \r
6555 /* Nonmodal error box */\r
6556 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6557                              WPARAM wParam, LPARAM lParam);\r
6558 \r
6559 VOID\r
6560 ErrorPopUp(char *title, char *content)\r
6561 {\r
6562   FARPROC lpProc;\r
6563   char *p, *q;\r
6564   BOOLEAN modal = hwndMain == NULL;\r
6565 \r
6566   p = content;\r
6567   q = errorMessage;\r
6568   while (*p) {\r
6569     if (*p == '\n') {\r
6570       if (modal) {\r
6571         *q++ = ' ';\r
6572         p++;\r
6573       } else {\r
6574         *q++ = '\r';\r
6575         *q++ = *p++;\r
6576       }\r
6577     } else {\r
6578       *q++ = *p++;\r
6579     }\r
6580   }\r
6581   *q = NULLCHAR;\r
6582   strncpy(errorTitle, title, sizeof(errorTitle));\r
6583   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6584   \r
6585   if (modal) {\r
6586     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6587   } else {\r
6588     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6589     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6590                  hwndMain, (DLGPROC)lpProc);\r
6591     FreeProcInstance(lpProc);\r
6592   }\r
6593 }\r
6594 \r
6595 VOID\r
6596 ErrorPopDown()\r
6597 {\r
6598   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6599   if (errorDialog == NULL) return;\r
6600   DestroyWindow(errorDialog);\r
6601   errorDialog = NULL;\r
6602   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6603 }\r
6604 \r
6605 LRESULT CALLBACK\r
6606 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6607 {\r
6608   HANDLE hwndText;\r
6609   RECT rChild;\r
6610 \r
6611   switch (message) {\r
6612   case WM_INITDIALOG:\r
6613     GetWindowRect(hDlg, &rChild);\r
6614 \r
6615     /*\r
6616     SetWindowPos(hDlg, NULL, rChild.left,\r
6617       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6618       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6619     */\r
6620 \r
6621     /* \r
6622         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6623         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6624         and it doesn't work when you resize the dialog.\r
6625         For now, just give it a default position.\r
6626     */\r
6627     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6628     Translate(hDlg, DLG_Error);\r
6629 \r
6630     errorDialog = hDlg;\r
6631     SetWindowText(hDlg, errorTitle);\r
6632     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6633     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6634     return FALSE;\r
6635 \r
6636   case WM_COMMAND:\r
6637     switch (LOWORD(wParam)) {\r
6638     case IDOK:\r
6639     case IDCANCEL:\r
6640       if (errorDialog == hDlg) errorDialog = NULL;\r
6641       DestroyWindow(hDlg);\r
6642       return TRUE;\r
6643 \r
6644     default:\r
6645       break;\r
6646     }\r
6647     break;\r
6648   }\r
6649   return FALSE;\r
6650 }\r
6651 \r
6652 #ifdef GOTHIC\r
6653 HWND gothicDialog = NULL;\r
6654 \r
6655 LRESULT CALLBACK\r
6656 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6657 {\r
6658   HANDLE hwndText;\r
6659   RECT rChild;\r
6660   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6661 \r
6662   switch (message) {\r
6663   case WM_INITDIALOG:\r
6664     GetWindowRect(hDlg, &rChild);\r
6665 \r
6666     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6667                                                              SWP_NOZORDER);\r
6668 \r
6669     /* \r
6670         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6671         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6672         and it doesn't work when you resize the dialog.\r
6673         For now, just give it a default position.\r
6674     */\r
6675     gothicDialog = hDlg;\r
6676     SetWindowText(hDlg, errorTitle);\r
6677     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6678     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6679     return FALSE;\r
6680 \r
6681   case WM_COMMAND:\r
6682     switch (LOWORD(wParam)) {\r
6683     case IDOK:\r
6684     case IDCANCEL:\r
6685       if (errorDialog == hDlg) errorDialog = NULL;\r
6686       DestroyWindow(hDlg);\r
6687       return TRUE;\r
6688 \r
6689     default:\r
6690       break;\r
6691     }\r
6692     break;\r
6693   }\r
6694   return FALSE;\r
6695 }\r
6696 \r
6697 VOID\r
6698 GothicPopUp(char *title, VariantClass variant)\r
6699 {\r
6700   FARPROC lpProc;\r
6701   static char *lastTitle;\r
6702 \r
6703   strncpy(errorTitle, title, sizeof(errorTitle));\r
6704   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6705 \r
6706   if(lastTitle != title && gothicDialog != NULL) {\r
6707     DestroyWindow(gothicDialog);\r
6708     gothicDialog = NULL;\r
6709   }\r
6710   if(variant != VariantNormal && gothicDialog == NULL) {\r
6711     title = lastTitle;\r
6712     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6713     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6714                  hwndMain, (DLGPROC)lpProc);\r
6715     FreeProcInstance(lpProc);\r
6716   }\r
6717 }\r
6718 #endif\r
6719 \r
6720 /*---------------------------------------------------------------------------*\\r
6721  *\r
6722  *  Ics Interaction console functions\r
6723  *\r
6724 \*---------------------------------------------------------------------------*/\r
6725 \r
6726 #define HISTORY_SIZE 64\r
6727 static char *history[HISTORY_SIZE];\r
6728 int histIn = 0, histP = 0;\r
6729 \r
6730 VOID\r
6731 SaveInHistory(char *cmd)\r
6732 {\r
6733   if (history[histIn] != NULL) {\r
6734     free(history[histIn]);\r
6735     history[histIn] = NULL;\r
6736   }\r
6737   if (*cmd == NULLCHAR) return;\r
6738   history[histIn] = StrSave(cmd);\r
6739   histIn = (histIn + 1) % HISTORY_SIZE;\r
6740   if (history[histIn] != NULL) {\r
6741     free(history[histIn]);\r
6742     history[histIn] = NULL;\r
6743   }\r
6744   histP = histIn;\r
6745 }\r
6746 \r
6747 char *\r
6748 PrevInHistory(char *cmd)\r
6749 {\r
6750   int newhp;\r
6751   if (histP == histIn) {\r
6752     if (history[histIn] != NULL) free(history[histIn]);\r
6753     history[histIn] = StrSave(cmd);\r
6754   }\r
6755   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6756   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6757   histP = newhp;\r
6758   return history[histP];\r
6759 }\r
6760 \r
6761 char *\r
6762 NextInHistory()\r
6763 {\r
6764   if (histP == histIn) return NULL;\r
6765   histP = (histP + 1) % HISTORY_SIZE;\r
6766   return history[histP];   \r
6767 }\r
6768 \r
6769 HMENU\r
6770 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6771 {\r
6772   HMENU hmenu, h;\r
6773   int i = 0;\r
6774   hmenu = LoadMenu(hInst, "TextMenu");\r
6775   h = GetSubMenu(hmenu, 0);\r
6776   while (e->item) {\r
6777     if (strcmp(e->item, "-") == 0) {\r
6778       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6779     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6780       int flags = MF_STRING, j = 0;\r
6781       if (e->item[0] == '|') {\r
6782         flags |= MF_MENUBARBREAK;\r
6783         j++;\r
6784       }\r
6785       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6786       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6787     }\r
6788     e++;\r
6789     i++;\r
6790   } \r
6791   return hmenu;\r
6792 }\r
6793 \r
6794 WNDPROC consoleTextWindowProc;\r
6795 \r
6796 void\r
6797 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6798 {\r
6799   char buf[MSG_SIZ], name[MSG_SIZ];\r
6800   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6801   CHARRANGE sel;\r
6802 \r
6803   if (!getname) {\r
6804     SetWindowText(hInput, command);\r
6805     if (immediate) {\r
6806       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6807     } else {\r
6808       sel.cpMin = 999999;\r
6809       sel.cpMax = 999999;\r
6810       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6811       SetFocus(hInput);\r
6812     }\r
6813     return;\r
6814   }    \r
6815   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6816   if (sel.cpMin == sel.cpMax) {\r
6817     /* Expand to surrounding word */\r
6818     TEXTRANGE tr;\r
6819     do {\r
6820       tr.chrg.cpMax = sel.cpMin;\r
6821       tr.chrg.cpMin = --sel.cpMin;\r
6822       if (sel.cpMin < 0) break;\r
6823       tr.lpstrText = name;\r
6824       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6825     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6826     sel.cpMin++;\r
6827 \r
6828     do {\r
6829       tr.chrg.cpMin = sel.cpMax;\r
6830       tr.chrg.cpMax = ++sel.cpMax;\r
6831       tr.lpstrText = name;\r
6832       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6833     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6834     sel.cpMax--;\r
6835 \r
6836     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6837       MessageBeep(MB_ICONEXCLAMATION);\r
6838       return;\r
6839     }\r
6840     tr.chrg = sel;\r
6841     tr.lpstrText = name;\r
6842     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6843   } else {\r
6844     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6845       MessageBeep(MB_ICONEXCLAMATION);\r
6846       return;\r
6847     }\r
6848     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6849   }\r
6850   if (immediate) {\r
6851     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6852     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6853     SetWindowText(hInput, buf);\r
6854     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6855   } else {\r
6856     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6857       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6858     SetWindowText(hInput, buf);\r
6859     sel.cpMin = 999999;\r
6860     sel.cpMax = 999999;\r
6861     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6862     SetFocus(hInput);\r
6863   }\r
6864 }\r
6865 \r
6866 LRESULT CALLBACK \r
6867 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6868 {\r
6869   HWND hInput;\r
6870   CHARRANGE sel;\r
6871 \r
6872   switch (message) {\r
6873   case WM_KEYDOWN:\r
6874     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6875     switch (wParam) {\r
6876     case VK_PRIOR:\r
6877       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6878       return 0;\r
6879     case VK_NEXT:\r
6880       sel.cpMin = 999999;\r
6881       sel.cpMax = 999999;\r
6882       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6883       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6884       return 0;\r
6885     }\r
6886     break;\r
6887   case WM_CHAR:\r
6888    if(wParam != '\022') {\r
6889     if (wParam == '\t') {\r
6890       if (GetKeyState(VK_SHIFT) < 0) {\r
6891         /* shifted */\r
6892         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6893         if (buttonDesc[0].hwnd) {\r
6894           SetFocus(buttonDesc[0].hwnd);\r
6895         } else {\r
6896           SetFocus(hwndMain);\r
6897         }\r
6898       } else {\r
6899         /* unshifted */\r
6900         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6901       }\r
6902     } else {\r
6903       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6904       JAWS_DELETE( SetFocus(hInput); )\r
6905       SendMessage(hInput, message, wParam, lParam);\r
6906     }\r
6907     return 0;\r
6908    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
6909    lParam = -1;\r
6910   case WM_RBUTTONDOWN:\r
6911     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6912       /* Move selection here if it was empty */\r
6913       POINT pt;\r
6914       pt.x = LOWORD(lParam);\r
6915       pt.y = HIWORD(lParam);\r
6916       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6917       if (sel.cpMin == sel.cpMax) {\r
6918         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6919         sel.cpMax = sel.cpMin;\r
6920         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6921       }\r
6922       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6923 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6924       POINT pt;\r
6925       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6926       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6927       if (sel.cpMin == sel.cpMax) {\r
6928         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6929         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6930       }\r
6931       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6932         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6933       }\r
6934       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6935       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6936       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6937       MenuPopup(hwnd, pt, hmenu, -1);\r
6938 }\r
6939     }\r
6940     return 0;\r
6941   case WM_RBUTTONUP:\r
6942     if (GetKeyState(VK_SHIFT) & ~1) {\r
6943       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6944         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6945     }\r
6946     return 0;\r
6947   case WM_PASTE:\r
6948     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6949     SetFocus(hInput);\r
6950     return SendMessage(hInput, message, wParam, lParam);\r
6951   case WM_MBUTTONDOWN:\r
6952     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6953   case WM_COMMAND:\r
6954     switch (LOWORD(wParam)) {\r
6955     case IDM_QuickPaste:\r
6956       {\r
6957         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6958         if (sel.cpMin == sel.cpMax) {\r
6959           MessageBeep(MB_ICONEXCLAMATION);\r
6960           return 0;\r
6961         }\r
6962         SendMessage(hwnd, WM_COPY, 0, 0);\r
6963         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6964         SendMessage(hInput, WM_PASTE, 0, 0);\r
6965         SetFocus(hInput);\r
6966         return 0;\r
6967       }\r
6968     case IDM_Cut:\r
6969       SendMessage(hwnd, WM_CUT, 0, 0);\r
6970       return 0;\r
6971     case IDM_Paste:\r
6972       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6973       return 0;\r
6974     case IDM_Copy:\r
6975       SendMessage(hwnd, WM_COPY, 0, 0);\r
6976       return 0;\r
6977     default:\r
6978       {\r
6979         int i = LOWORD(wParam) - IDM_CommandX;\r
6980         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6981             icsTextMenuEntry[i].command != NULL) {\r
6982           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6983                    icsTextMenuEntry[i].getname,\r
6984                    icsTextMenuEntry[i].immediate);\r
6985           return 0;\r
6986         }\r
6987       }\r
6988       break;\r
6989     }\r
6990     break;\r
6991   }\r
6992   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
6993 }\r
6994 \r
6995 WNDPROC consoleInputWindowProc;\r
6996 \r
6997 LRESULT CALLBACK\r
6998 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6999 {\r
7000   char buf[MSG_SIZ];\r
7001   char *p;\r
7002   static BOOL sendNextChar = FALSE;\r
7003   static BOOL quoteNextChar = FALSE;\r
7004   InputSource *is = consoleInputSource;\r
7005   CHARFORMAT cf;\r
7006   CHARRANGE sel;\r
7007 \r
7008   switch (message) {\r
7009   case WM_CHAR:\r
7010     if (!appData.localLineEditing || sendNextChar) {\r
7011       is->buf[0] = (CHAR) wParam;\r
7012       is->count = 1;\r
7013       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7014       sendNextChar = FALSE;\r
7015       return 0;\r
7016     }\r
7017     if (quoteNextChar) {\r
7018       buf[0] = (char) wParam;\r
7019       buf[1] = NULLCHAR;\r
7020       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7021       quoteNextChar = FALSE;\r
7022       return 0;\r
7023     }\r
7024     switch (wParam) {\r
7025     case '\r':   /* Enter key */\r
7026       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7027       if (consoleEcho) SaveInHistory(is->buf);\r
7028       is->buf[is->count++] = '\n';\r
7029       is->buf[is->count] = NULLCHAR;\r
7030       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7031       if (consoleEcho) {\r
7032         ConsoleOutput(is->buf, is->count, TRUE);\r
7033       } else if (appData.localLineEditing) {\r
7034         ConsoleOutput("\n", 1, TRUE);\r
7035       }\r
7036       /* fall thru */\r
7037     case '\033': /* Escape key */\r
7038       SetWindowText(hwnd, "");\r
7039       cf.cbSize = sizeof(CHARFORMAT);\r
7040       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7041       if (consoleEcho) {\r
7042         cf.crTextColor = textAttribs[ColorNormal].color;\r
7043       } else {\r
7044         cf.crTextColor = COLOR_ECHOOFF;\r
7045       }\r
7046       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7047       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7048       return 0;\r
7049     case '\t':   /* Tab key */\r
7050       if (GetKeyState(VK_SHIFT) < 0) {\r
7051         /* shifted */\r
7052         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7053       } else {\r
7054         /* unshifted */\r
7055         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7056         if (buttonDesc[0].hwnd) {\r
7057           SetFocus(buttonDesc[0].hwnd);\r
7058         } else {\r
7059           SetFocus(hwndMain);\r
7060         }\r
7061       }\r
7062       return 0;\r
7063     case '\023': /* Ctrl+S */\r
7064       sendNextChar = TRUE;\r
7065       return 0;\r
7066     case '\021': /* Ctrl+Q */\r
7067       quoteNextChar = TRUE;\r
7068       return 0;\r
7069     JAWS_REPLAY\r
7070     default:\r
7071       break;\r
7072     }\r
7073     break;\r
7074   case WM_KEYDOWN:\r
7075     switch (wParam) {\r
7076     case VK_UP:\r
7077       GetWindowText(hwnd, buf, MSG_SIZ);\r
7078       p = PrevInHistory(buf);\r
7079       if (p != NULL) {\r
7080         SetWindowText(hwnd, p);\r
7081         sel.cpMin = 999999;\r
7082         sel.cpMax = 999999;\r
7083         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7084         return 0;\r
7085       }\r
7086       break;\r
7087     case VK_DOWN:\r
7088       p = NextInHistory();\r
7089       if (p != NULL) {\r
7090         SetWindowText(hwnd, p);\r
7091         sel.cpMin = 999999;\r
7092         sel.cpMax = 999999;\r
7093         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7094         return 0;\r
7095       }\r
7096       break;\r
7097     case VK_HOME:\r
7098     case VK_END:\r
7099       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7100       /* fall thru */\r
7101     case VK_PRIOR:\r
7102     case VK_NEXT:\r
7103       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7104       return 0;\r
7105     }\r
7106     break;\r
7107   case WM_MBUTTONDOWN:\r
7108     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7109       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7110     break;\r
7111   case WM_RBUTTONUP:\r
7112     if (GetKeyState(VK_SHIFT) & ~1) {\r
7113       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7114         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7115     } else {\r
7116       POINT pt;\r
7117       HMENU hmenu;\r
7118       hmenu = LoadMenu(hInst, "InputMenu");\r
7119       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7120       if (sel.cpMin == sel.cpMax) {\r
7121         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7122         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7123       }\r
7124       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7125         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7126       }\r
7127       pt.x = LOWORD(lParam);\r
7128       pt.y = HIWORD(lParam);\r
7129       MenuPopup(hwnd, pt, hmenu, -1);\r
7130     }\r
7131     return 0;\r
7132   case WM_COMMAND:\r
7133     switch (LOWORD(wParam)) { \r
7134     case IDM_Undo:\r
7135       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7136       return 0;\r
7137     case IDM_SelectAll:\r
7138       sel.cpMin = 0;\r
7139       sel.cpMax = -1; /*999999?*/\r
7140       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7141       return 0;\r
7142     case IDM_Cut:\r
7143       SendMessage(hwnd, WM_CUT, 0, 0);\r
7144       return 0;\r
7145     case IDM_Paste:\r
7146       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7147       return 0;\r
7148     case IDM_Copy:\r
7149       SendMessage(hwnd, WM_COPY, 0, 0);\r
7150       return 0;\r
7151     }\r
7152     break;\r
7153   }\r
7154   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7155 }\r
7156 \r
7157 #define CO_MAX  100000\r
7158 #define CO_TRIM   1000\r
7159 \r
7160 LRESULT CALLBACK\r
7161 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7162 {\r
7163   static SnapData sd;\r
7164   HWND hText, hInput;\r
7165   RECT rect;\r
7166   static int sizeX, sizeY;\r
7167   int newSizeX, newSizeY;\r
7168   MINMAXINFO *mmi;\r
7169   WORD wMask;\r
7170 \r
7171   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7172   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7173 \r
7174   switch (message) {\r
7175   case WM_NOTIFY:\r
7176     if (((NMHDR*)lParam)->code == EN_LINK)\r
7177     {\r
7178       ENLINK *pLink = (ENLINK*)lParam;\r
7179       if (pLink->msg == WM_LBUTTONUP)\r
7180       {\r
7181         TEXTRANGE tr;\r
7182 \r
7183         tr.chrg = pLink->chrg;\r
7184         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7185         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7186         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7187         free(tr.lpstrText);\r
7188       }\r
7189     }\r
7190     break;\r
7191   case WM_INITDIALOG: /* message: initialize dialog box */\r
7192     hwndConsole = hDlg;\r
7193     SetFocus(hInput);\r
7194     consoleTextWindowProc = (WNDPROC)\r
7195       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
7196     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7197     consoleInputWindowProc = (WNDPROC)\r
7198       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
7199     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7200     Colorize(ColorNormal, TRUE);\r
7201     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7202     ChangedConsoleFont();\r
7203     GetClientRect(hDlg, &rect);\r
7204     sizeX = rect.right;\r
7205     sizeY = rect.bottom;\r
7206     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7207         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7208       WINDOWPLACEMENT wp;\r
7209       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7210       wp.length = sizeof(WINDOWPLACEMENT);\r
7211       wp.flags = 0;\r
7212       wp.showCmd = SW_SHOW;\r
7213       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7214       wp.rcNormalPosition.left = wpConsole.x;\r
7215       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7216       wp.rcNormalPosition.top = wpConsole.y;\r
7217       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7218       SetWindowPlacement(hDlg, &wp);\r
7219     }\r
7220 \r
7221    // [HGM] Chessknight's change 2004-07-13\r
7222    else { /* Determine Defaults */\r
7223        WINDOWPLACEMENT wp;\r
7224        wpConsole.x = wpMain.width + 1;\r
7225        wpConsole.y = wpMain.y;\r
7226        wpConsole.width = screenWidth -  wpMain.width;\r
7227        wpConsole.height = wpMain.height;\r
7228        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7229        wp.length = sizeof(WINDOWPLACEMENT);\r
7230        wp.flags = 0;\r
7231        wp.showCmd = SW_SHOW;\r
7232        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7233        wp.rcNormalPosition.left = wpConsole.x;\r
7234        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7235        wp.rcNormalPosition.top = wpConsole.y;\r
7236        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7237        SetWindowPlacement(hDlg, &wp);\r
7238     }\r
7239 \r
7240    // Allow hText to highlight URLs and send notifications on them\r
7241    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7242    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7243    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7244    SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width\r
7245 \r
7246     return FALSE;\r
7247 \r
7248   case WM_SETFOCUS:\r
7249     SetFocus(hInput);\r
7250     return 0;\r
7251 \r
7252   case WM_CLOSE:\r
7253     ExitEvent(0);\r
7254     /* not reached */\r
7255     break;\r
7256 \r
7257   case WM_SIZE:\r
7258     if (IsIconic(hDlg)) break;\r
7259     newSizeX = LOWORD(lParam);\r
7260     newSizeY = HIWORD(lParam);\r
7261     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7262       RECT rectText, rectInput;\r
7263       POINT pt;\r
7264       int newTextHeight, newTextWidth;\r
7265       GetWindowRect(hText, &rectText);\r
7266       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7267       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7268       if (newTextHeight < 0) {\r
7269         newSizeY += -newTextHeight;\r
7270         newTextHeight = 0;\r
7271       }\r
7272       SetWindowPos(hText, NULL, 0, 0,\r
7273         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7274       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7275       pt.x = rectInput.left;\r
7276       pt.y = rectInput.top + newSizeY - sizeY;\r
7277       ScreenToClient(hDlg, &pt);\r
7278       SetWindowPos(hInput, NULL, \r
7279         pt.x, pt.y, /* needs client coords */   \r
7280         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7281         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7282     }\r
7283     sizeX = newSizeX;\r
7284     sizeY = newSizeY;\r
7285     break;\r
7286 \r
7287   case WM_GETMINMAXINFO:\r
7288     /* Prevent resizing window too small */\r
7289     mmi = (MINMAXINFO *) lParam;\r
7290     mmi->ptMinTrackSize.x = 100;\r
7291     mmi->ptMinTrackSize.y = 100;\r
7292     break;\r
7293 \r
7294   /* [AS] Snapping */\r
7295   case WM_ENTERSIZEMOVE:\r
7296     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7297 \r
7298   case WM_SIZING:\r
7299     return OnSizing( &sd, hDlg, wParam, lParam );\r
7300 \r
7301   case WM_MOVING:\r
7302     return OnMoving( &sd, hDlg, wParam, lParam );\r
7303 \r
7304   case WM_EXITSIZEMOVE:\r
7305         UpdateICSWidth(hText);\r
7306     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7307   }\r
7308 \r
7309   return DefWindowProc(hDlg, message, wParam, lParam);\r
7310 }\r
7311 \r
7312 \r
7313 VOID\r
7314 ConsoleCreate()\r
7315 {\r
7316   HWND hCons;\r
7317   if (hwndConsole) return;\r
7318   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7319   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7320 }\r
7321 \r
7322 \r
7323 VOID\r
7324 ConsoleOutput(char* data, int length, int forceVisible)\r
7325 {\r
7326   HWND hText;\r
7327   int trim, exlen;\r
7328   char *p, *q;\r
7329   char buf[CO_MAX+1];\r
7330   POINT pEnd;\r
7331   RECT rect;\r
7332   static int delayLF = 0;\r
7333   CHARRANGE savesel, sel;\r
7334 \r
7335   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7336   p = data;\r
7337   q = buf;\r
7338   if (delayLF) {\r
7339     *q++ = '\r';\r
7340     *q++ = '\n';\r
7341     delayLF = 0;\r
7342   }\r
7343   while (length--) {\r
7344     if (*p == '\n') {\r
7345       if (*++p) {\r
7346         *q++ = '\r';\r
7347         *q++ = '\n';\r
7348       } else {\r
7349         delayLF = 1;\r
7350       }\r
7351     } else if (*p == '\007') {\r
7352        MyPlaySound(&sounds[(int)SoundBell]);\r
7353        p++;\r
7354     } else {\r
7355       *q++ = *p++;\r
7356     }\r
7357   }\r
7358   *q = NULLCHAR;\r
7359   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7360   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7361   /* Save current selection */\r
7362   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7363   exlen = GetWindowTextLength(hText);\r
7364   /* Find out whether current end of text is visible */\r
7365   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7366   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7367   /* Trim existing text if it's too long */\r
7368   if (exlen + (q - buf) > CO_MAX) {\r
7369     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7370     sel.cpMin = 0;\r
7371     sel.cpMax = trim;\r
7372     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7373     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7374     exlen -= trim;\r
7375     savesel.cpMin -= trim;\r
7376     savesel.cpMax -= trim;\r
7377     if (exlen < 0) exlen = 0;\r
7378     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7379     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7380   }\r
7381   /* Append the new text */\r
7382   sel.cpMin = exlen;\r
7383   sel.cpMax = exlen;\r
7384   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7385   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7386   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7387   if (forceVisible || exlen == 0 ||\r
7388       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7389        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7390     /* Scroll to make new end of text visible if old end of text\r
7391        was visible or new text is an echo of user typein */\r
7392     sel.cpMin = 9999999;\r
7393     sel.cpMax = 9999999;\r
7394     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7395     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7396     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7397     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7398   }\r
7399   if (savesel.cpMax == exlen || forceVisible) {\r
7400     /* Move insert point to new end of text if it was at the old\r
7401        end of text or if the new text is an echo of user typein */\r
7402     sel.cpMin = 9999999;\r
7403     sel.cpMax = 9999999;\r
7404     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7405   } else {\r
7406     /* Restore previous selection */\r
7407     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7408   }\r
7409   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7410 }\r
7411 \r
7412 /*---------*/\r
7413 \r
7414 \r
7415 void\r
7416 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7417 {\r
7418   char buf[100];\r
7419   char *str;\r
7420   COLORREF oldFg, oldBg;\r
7421   HFONT oldFont;\r
7422   RECT rect;\r
7423 \r
7424   if(copyNumber > 1)
7425     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7426 \r
7427   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7428   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7429   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7430 \r
7431   rect.left = x;\r
7432   rect.right = x + squareSize;\r
7433   rect.top  = y;\r
7434   rect.bottom = y + squareSize;\r
7435   str = buf;\r
7436 \r
7437   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7438                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7439              y, ETO_CLIPPED|ETO_OPAQUE,\r
7440              &rect, str, strlen(str), NULL);\r
7441 \r
7442   (void) SetTextColor(hdc, oldFg);\r
7443   (void) SetBkColor(hdc, oldBg);\r
7444   (void) SelectObject(hdc, oldFont);\r
7445 }\r
7446 \r
7447 void\r
7448 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7449               RECT *rect, char *color, char *flagFell)\r
7450 {\r
7451   char buf[100];\r
7452   char *str;\r
7453   COLORREF oldFg, oldBg;\r
7454   HFONT oldFont;\r
7455 \r
7456   if (appData.clockMode) {\r
7457     if (tinyLayout)\r
7458       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7459     else\r
7460       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7461     str = buf;\r
7462   } else {\r
7463     str = color;\r
7464   }\r
7465 \r
7466   if (highlight) {\r
7467     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7468     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7469   } else {\r
7470     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7471     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7472   }\r
7473   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7474 \r
7475   JAWS_SILENCE\r
7476 \r
7477   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7478              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7479              rect, str, strlen(str), NULL);\r
7480   if(logoHeight > 0 && appData.clockMode) {\r
7481       RECT r;\r
7482       str += strlen(color)+2;\r
7483       r.top = rect->top + logoHeight/2;\r
7484       r.left = rect->left;\r
7485       r.right = rect->right;\r
7486       r.bottom = rect->bottom;\r
7487       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7488                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7489                  &r, str, strlen(str), NULL);\r
7490   }\r
7491   (void) SetTextColor(hdc, oldFg);\r
7492   (void) SetBkColor(hdc, oldBg);\r
7493   (void) SelectObject(hdc, oldFont);\r
7494 }\r
7495 \r
7496 \r
7497 int\r
7498 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7499            OVERLAPPED *ovl)\r
7500 {\r
7501   int ok, err;\r
7502 \r
7503   /* [AS]  */\r
7504   if( count <= 0 ) {\r
7505     if (appData.debugMode) {\r
7506       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7507     }\r
7508 \r
7509     return ERROR_INVALID_USER_BUFFER;\r
7510   }\r
7511 \r
7512   ResetEvent(ovl->hEvent);\r
7513   ovl->Offset = ovl->OffsetHigh = 0;\r
7514   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7515   if (ok) {\r
7516     err = NO_ERROR;\r
7517   } else {\r
7518     err = GetLastError();\r
7519     if (err == ERROR_IO_PENDING) {\r
7520       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7521       if (ok)\r
7522         err = NO_ERROR;\r
7523       else\r
7524         err = GetLastError();\r
7525     }\r
7526   }\r
7527   return err;\r
7528 }\r
7529 \r
7530 int\r
7531 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7532             OVERLAPPED *ovl)\r
7533 {\r
7534   int ok, err;\r
7535 \r
7536   ResetEvent(ovl->hEvent);\r
7537   ovl->Offset = ovl->OffsetHigh = 0;\r
7538   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7539   if (ok) {\r
7540     err = NO_ERROR;\r
7541   } else {\r
7542     err = GetLastError();\r
7543     if (err == ERROR_IO_PENDING) {\r
7544       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7545       if (ok)\r
7546         err = NO_ERROR;\r
7547       else\r
7548         err = GetLastError();\r
7549     }\r
7550   }\r
7551   return err;\r
7552 }\r
7553 \r
7554 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7555 void CheckForInputBufferFull( InputSource * is )\r
7556 {\r
7557     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7558         /* Look for end of line */\r
7559         char * p = is->buf;\r
7560         \r
7561         while( p < is->next && *p != '\n' ) {\r
7562             p++;\r
7563         }\r
7564 \r
7565         if( p >= is->next ) {\r
7566             if (appData.debugMode) {\r
7567                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7568             }\r
7569 \r
7570             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7571             is->count = (DWORD) -1;\r
7572             is->next = is->buf;\r
7573         }\r
7574     }\r
7575 }\r
7576 \r
7577 DWORD\r
7578 InputThread(LPVOID arg)\r
7579 {\r
7580   InputSource *is;\r
7581   OVERLAPPED ovl;\r
7582 \r
7583   is = (InputSource *) arg;\r
7584   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7585   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7586   while (is->hThread != NULL) {\r
7587     is->error = DoReadFile(is->hFile, is->next,\r
7588                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7589                            &is->count, &ovl);\r
7590     if (is->error == NO_ERROR) {\r
7591       is->next += is->count;\r
7592     } else {\r
7593       if (is->error == ERROR_BROKEN_PIPE) {\r
7594         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7595         is->count = 0;\r
7596       } else {\r
7597         is->count = (DWORD) -1;\r
7598         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7599         break; \r
7600       }\r
7601     }\r
7602 \r
7603     CheckForInputBufferFull( is );\r
7604 \r
7605     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7606 \r
7607     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7608 \r
7609     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7610   }\r
7611 \r
7612   CloseHandle(ovl.hEvent);\r
7613   CloseHandle(is->hFile);\r
7614 \r
7615   if (appData.debugMode) {\r
7616     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7617   }\r
7618 \r
7619   return 0;\r
7620 }\r
7621 \r
7622 \r
7623 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7624 DWORD\r
7625 NonOvlInputThread(LPVOID arg)\r
7626 {\r
7627   InputSource *is;\r
7628   char *p, *q;\r
7629   int i;\r
7630   char prev;\r
7631 \r
7632   is = (InputSource *) arg;\r
7633   while (is->hThread != NULL) {\r
7634     is->error = ReadFile(is->hFile, is->next,\r
7635                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7636                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7637     if (is->error == NO_ERROR) {\r
7638       /* Change CRLF to LF */\r
7639       if (is->next > is->buf) {\r
7640         p = is->next - 1;\r
7641         i = is->count + 1;\r
7642       } else {\r
7643         p = is->next;\r
7644         i = is->count;\r
7645       }\r
7646       q = p;\r
7647       prev = NULLCHAR;\r
7648       while (i > 0) {\r
7649         if (prev == '\r' && *p == '\n') {\r
7650           *(q-1) = '\n';\r
7651           is->count--;\r
7652         } else { \r
7653           *q++ = *p;\r
7654         }\r
7655         prev = *p++;\r
7656         i--;\r
7657       }\r
7658       *q = NULLCHAR;\r
7659       is->next = q;\r
7660     } else {\r
7661       if (is->error == ERROR_BROKEN_PIPE) {\r
7662         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7663         is->count = 0; \r
7664       } else {\r
7665         is->count = (DWORD) -1;\r
7666       }\r
7667     }\r
7668 \r
7669     CheckForInputBufferFull( is );\r
7670 \r
7671     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7672 \r
7673     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7674 \r
7675     if (is->count < 0) break;  /* Quit on error */\r
7676   }\r
7677   CloseHandle(is->hFile);\r
7678   return 0;\r
7679 }\r
7680 \r
7681 DWORD\r
7682 SocketInputThread(LPVOID arg)\r
7683 {\r
7684   InputSource *is;\r
7685 \r
7686   is = (InputSource *) arg;\r
7687   while (is->hThread != NULL) {\r
7688     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7689     if ((int)is->count == SOCKET_ERROR) {\r
7690       is->count = (DWORD) -1;\r
7691       is->error = WSAGetLastError();\r
7692     } else {\r
7693       is->error = NO_ERROR;\r
7694       is->next += is->count;\r
7695       if (is->count == 0 && is->second == is) {\r
7696         /* End of file on stderr; quit with no message */\r
7697         break;\r
7698       }\r
7699     }\r
7700     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7701 \r
7702     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7703 \r
7704     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7705   }\r
7706   return 0;\r
7707 }\r
7708 \r
7709 VOID\r
7710 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7711 {\r
7712   InputSource *is;\r
7713 \r
7714   is = (InputSource *) lParam;\r
7715   if (is->lineByLine) {\r
7716     /* Feed in lines one by one */\r
7717     char *p = is->buf;\r
7718     char *q = p;\r
7719     while (q < is->next) {\r
7720       if (*q++ == '\n') {\r
7721         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7722         p = q;\r
7723       }\r
7724     }\r
7725     \r
7726     /* Move any partial line to the start of the buffer */\r
7727     q = is->buf;\r
7728     while (p < is->next) {\r
7729       *q++ = *p++;\r
7730     }\r
7731     is->next = q;\r
7732 \r
7733     if (is->error != NO_ERROR || is->count == 0) {\r
7734       /* Notify backend of the error.  Note: If there was a partial\r
7735          line at the end, it is not flushed through. */\r
7736       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7737     }\r
7738   } else {\r
7739     /* Feed in the whole chunk of input at once */\r
7740     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7741     is->next = is->buf;\r
7742   }\r
7743 }\r
7744 \r
7745 /*---------------------------------------------------------------------------*\\r
7746  *\r
7747  *  Menu enables. Used when setting various modes.\r
7748  *\r
7749 \*---------------------------------------------------------------------------*/\r
7750 \r
7751 typedef struct {\r
7752   int item;\r
7753   int flags;\r
7754 } Enables;\r
7755 \r
7756 VOID\r
7757 GreyRevert(Boolean grey)\r
7758 { // [HGM] vari: for retracting variations in local mode\r
7759   HMENU hmenu = GetMenu(hwndMain);\r
7760   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7761   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7762 }\r
7763 \r
7764 VOID\r
7765 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7766 {\r
7767   while (enab->item > 0) {\r
7768     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7769     enab++;\r
7770   }\r
7771 }\r
7772 \r
7773 Enables gnuEnables[] = {\r
7774   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7775   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7776   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7777   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7778   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7779   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7780   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7781   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7782   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7783   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7784   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7785   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7786   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7787   { -1, -1 }\r
7788 };\r
7789 \r
7790 Enables icsEnables[] = {\r
7791   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7792   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7793   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7794   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7795   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7796   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7797   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7798   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7799   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7800   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7801   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7802   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7803   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7804   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7805   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7806   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7807   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7808   { -1, -1 }\r
7809 };\r
7810 \r
7811 #if ZIPPY\r
7812 Enables zippyEnables[] = {\r
7813   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7814   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7815   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7816   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7817   { -1, -1 }\r
7818 };\r
7819 #endif\r
7820 \r
7821 Enables ncpEnables[] = {\r
7822   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7823   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7824   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7825   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7826   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7827   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7828   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7829   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7830   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7831   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7832   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7833   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7834   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7835   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7836   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7837   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7838   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7839   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7840   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7841   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7842   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7843   { -1, -1 }\r
7844 };\r
7845 \r
7846 Enables trainingOnEnables[] = {\r
7847   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7848   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7849   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7850   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7851   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7852   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7853   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7854   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7855   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7856   { -1, -1 }\r
7857 };\r
7858 \r
7859 Enables trainingOffEnables[] = {\r
7860   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7861   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7862   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7863   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7864   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7865   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7866   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7867   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7868   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7869   { -1, -1 }\r
7870 };\r
7871 \r
7872 /* These modify either ncpEnables or gnuEnables */\r
7873 Enables cmailEnables[] = {\r
7874   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7875   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7876   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7877   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7878   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7879   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7880   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7881   { -1, -1 }\r
7882 };\r
7883 \r
7884 Enables machineThinkingEnables[] = {\r
7885   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7886   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7887   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7888   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7889   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7890   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7891   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7892   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7893   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7894   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7895   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7896   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7897   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7898   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7899   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7900   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7901   { -1, -1 }\r
7902 };\r
7903 \r
7904 Enables userThinkingEnables[] = {\r
7905   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7906   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7907   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7908   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7909   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7910   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7911   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7912   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7913   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7914   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7915   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7916   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7917   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7918   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7919   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7920   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7921   { -1, -1 }\r
7922 };\r
7923 \r
7924 /*---------------------------------------------------------------------------*\\r
7925  *\r
7926  *  Front-end interface functions exported by XBoard.\r
7927  *  Functions appear in same order as prototypes in frontend.h.\r
7928  * \r
7929 \*---------------------------------------------------------------------------*/\r
7930 VOID\r
7931 ModeHighlight()\r
7932 {\r
7933   static UINT prevChecked = 0;\r
7934   static int prevPausing = 0;\r
7935   UINT nowChecked;\r
7936 \r
7937   if (pausing != prevPausing) {\r
7938     prevPausing = pausing;\r
7939     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7940                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7941     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7942   }\r
7943 \r
7944   switch (gameMode) {\r
7945   case BeginningOfGame:\r
7946     if (appData.icsActive)\r
7947       nowChecked = IDM_IcsClient;\r
7948     else if (appData.noChessProgram)\r
7949       nowChecked = IDM_EditGame;\r
7950     else\r
7951       nowChecked = IDM_MachineBlack;\r
7952     break;\r
7953   case MachinePlaysBlack:\r
7954     nowChecked = IDM_MachineBlack;\r
7955     break;\r
7956   case MachinePlaysWhite:\r
7957     nowChecked = IDM_MachineWhite;\r
7958     break;\r
7959   case TwoMachinesPlay:\r
7960     nowChecked = matchMode ? IDM_Match : IDM_TwoMachines; // [HGM] match\r
7961     break;\r
7962   case AnalyzeMode:\r
7963     nowChecked = IDM_AnalysisMode;\r
7964     break;\r
7965   case AnalyzeFile:\r
7966     nowChecked = IDM_AnalyzeFile;\r
7967     break;\r
7968   case EditGame:\r
7969     nowChecked = IDM_EditGame;\r
7970     break;\r
7971   case PlayFromGameFile:\r
7972     nowChecked = IDM_LoadGame;\r
7973     break;\r
7974   case EditPosition:\r
7975     nowChecked = IDM_EditPosition;\r
7976     break;\r
7977   case Training:\r
7978     nowChecked = IDM_Training;\r
7979     break;\r
7980   case IcsPlayingWhite:\r
7981   case IcsPlayingBlack:\r
7982   case IcsObserving:\r
7983   case IcsIdle:\r
7984     nowChecked = IDM_IcsClient;\r
7985     break;\r
7986   default:\r
7987   case EndOfGame:\r
7988     nowChecked = 0;\r
7989     break;\r
7990   }\r
7991   if (prevChecked != 0)\r
7992     (void) CheckMenuItem(GetMenu(hwndMain),\r
7993                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
7994   if (nowChecked != 0)\r
7995     (void) CheckMenuItem(GetMenu(hwndMain),\r
7996                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
7997 \r
7998   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
7999     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8000                           MF_BYCOMMAND|MF_ENABLED);\r
8001   } else {\r
8002     (void) EnableMenuItem(GetMenu(hwndMain), \r
8003                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8004   }\r
8005 \r
8006   prevChecked = nowChecked;\r
8007 \r
8008   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8009   if (appData.icsActive) {\r
8010        if (appData.icsEngineAnalyze) {\r
8011                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8012                        MF_BYCOMMAND|MF_CHECKED);\r
8013        } else {\r
8014                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8015                        MF_BYCOMMAND|MF_UNCHECKED);\r
8016        }\r
8017   }\r
8018 }\r
8019 \r
8020 VOID\r
8021 SetICSMode()\r
8022 {\r
8023   HMENU hmenu = GetMenu(hwndMain);\r
8024   SetMenuEnables(hmenu, icsEnables);\r
8025   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8026     MF_BYPOSITION|MF_ENABLED);\r
8027 #if ZIPPY\r
8028   if (appData.zippyPlay) {\r
8029     SetMenuEnables(hmenu, zippyEnables);\r
8030     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8031          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8032           MF_BYCOMMAND|MF_ENABLED);\r
8033   }\r
8034 #endif\r
8035 }\r
8036 \r
8037 VOID\r
8038 SetGNUMode()\r
8039 {\r
8040   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8041 }\r
8042 \r
8043 VOID\r
8044 SetNCPMode()\r
8045 {\r
8046   HMENU hmenu = GetMenu(hwndMain);\r
8047   SetMenuEnables(hmenu, ncpEnables);\r
8048   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8049     MF_BYPOSITION|MF_GRAYED);\r
8050     DrawMenuBar(hwndMain);\r
8051 }\r
8052 \r
8053 VOID\r
8054 SetCmailMode()\r
8055 {\r
8056   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8057 }\r
8058 \r
8059 VOID \r
8060 SetTrainingModeOn()\r
8061 {\r
8062   int i;\r
8063   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8064   for (i = 0; i < N_BUTTONS; i++) {\r
8065     if (buttonDesc[i].hwnd != NULL)\r
8066       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8067   }\r
8068   CommentPopDown();\r
8069 }\r
8070 \r
8071 VOID SetTrainingModeOff()\r
8072 {\r
8073   int i;\r
8074   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8075   for (i = 0; i < N_BUTTONS; i++) {\r
8076     if (buttonDesc[i].hwnd != NULL)\r
8077       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8078   }\r
8079 }\r
8080 \r
8081 \r
8082 VOID\r
8083 SetUserThinkingEnables()\r
8084 {\r
8085   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8086 }\r
8087 \r
8088 VOID\r
8089 SetMachineThinkingEnables()\r
8090 {\r
8091   HMENU hMenu = GetMenu(hwndMain);\r
8092   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8093 \r
8094   SetMenuEnables(hMenu, machineThinkingEnables);\r
8095 \r
8096   if (gameMode == MachinePlaysBlack) {\r
8097     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8098   } else if (gameMode == MachinePlaysWhite) {\r
8099     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8100   } else if (gameMode == TwoMachinesPlay) {\r
8101     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8102   }\r
8103 }\r
8104 \r
8105 \r
8106 VOID\r
8107 DisplayTitle(char *str)\r
8108 {\r
8109   char title[MSG_SIZ], *host;\r
8110   if (str[0] != NULLCHAR) {\r
8111     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8112   } else if (appData.icsActive) {\r
8113     if (appData.icsCommPort[0] != NULLCHAR)\r
8114       host = "ICS";\r
8115     else \r
8116       host = appData.icsHost;\r
8117       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8118   } else if (appData.noChessProgram) {\r
8119     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8120   } else {\r
8121     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8122     strcat(title, ": ");\r
8123     strcat(title, first.tidy);\r
8124   }\r
8125   SetWindowText(hwndMain, title);\r
8126 }\r
8127 \r
8128 \r
8129 VOID\r
8130 DisplayMessage(char *str1, char *str2)\r
8131 {\r
8132   HDC hdc;\r
8133   HFONT oldFont;\r
8134   int remain = MESSAGE_TEXT_MAX - 1;\r
8135   int len;\r
8136 \r
8137   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8138   messageText[0] = NULLCHAR;\r
8139   if (*str1) {\r
8140     len = strlen(str1);\r
8141     if (len > remain) len = remain;\r
8142     strncpy(messageText, str1, len);\r
8143     messageText[len] = NULLCHAR;\r
8144     remain -= len;\r
8145   }\r
8146   if (*str2 && remain >= 2) {\r
8147     if (*str1) {\r
8148       strcat(messageText, "  ");\r
8149       remain -= 2;\r
8150     }\r
8151     len = strlen(str2);\r
8152     if (len > remain) len = remain;\r
8153     strncat(messageText, str2, len);\r
8154   }\r
8155   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8156 \r
8157   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8158 \r
8159   SAYMACHINEMOVE();\r
8160 \r
8161   hdc = GetDC(hwndMain);\r
8162   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8163   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8164              &messageRect, messageText, strlen(messageText), NULL);\r
8165   (void) SelectObject(hdc, oldFont);\r
8166   (void) ReleaseDC(hwndMain, hdc);\r
8167 }\r
8168 \r
8169 VOID\r
8170 DisplayError(char *str, int error)\r
8171 {\r
8172   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8173   int len;\r
8174 \r
8175   if (error == 0) {\r
8176     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8177   } else {\r
8178     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8179                         NULL, error, LANG_NEUTRAL,\r
8180                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8181     if (len > 0) {\r
8182       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8183     } else {\r
8184       ErrorMap *em = errmap;\r
8185       while (em->err != 0 && em->err != error) em++;\r
8186       if (em->err != 0) {\r
8187         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8188       } else {\r
8189         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8190       }\r
8191     }\r
8192   }\r
8193   \r
8194   ErrorPopUp(_("Error"), buf);\r
8195 }\r
8196 \r
8197 \r
8198 VOID\r
8199 DisplayMoveError(char *str)\r
8200 {\r
8201   fromX = fromY = -1;\r
8202   ClearHighlights();\r
8203   DrawPosition(FALSE, NULL);\r
8204   if (appData.popupMoveErrors) {\r
8205     ErrorPopUp(_("Error"), str);\r
8206   } else {\r
8207     DisplayMessage(str, "");\r
8208     moveErrorMessageUp = TRUE;\r
8209   }\r
8210 }\r
8211 \r
8212 VOID\r
8213 DisplayFatalError(char *str, int error, int exitStatus)\r
8214 {\r
8215   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8216   int len;\r
8217   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8218 \r
8219   if (error != 0) {\r
8220     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8221                         NULL, error, LANG_NEUTRAL,\r
8222                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8223     if (len > 0) {\r
8224       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8225     } else {\r
8226       ErrorMap *em = errmap;\r
8227       while (em->err != 0 && em->err != error) em++;\r
8228       if (em->err != 0) {\r
8229         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8230       } else {\r
8231         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8232       }\r
8233     }\r
8234     str = buf;\r
8235   }\r
8236   if (appData.debugMode) {\r
8237     fprintf(debugFP, "%s: %s\n", label, str);\r
8238   }\r
8239   if (appData.popupExitMessage) {\r
8240     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8241                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8242   }\r
8243   ExitEvent(exitStatus);\r
8244 }\r
8245 \r
8246 \r
8247 VOID\r
8248 DisplayInformation(char *str)\r
8249 {\r
8250   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8251 }\r
8252 \r
8253 \r
8254 VOID\r
8255 DisplayNote(char *str)\r
8256 {\r
8257   ErrorPopUp(_("Note"), str);\r
8258 }\r
8259 \r
8260 \r
8261 typedef struct {\r
8262   char *title, *question, *replyPrefix;\r
8263   ProcRef pr;\r
8264 } QuestionParams;\r
8265 \r
8266 LRESULT CALLBACK\r
8267 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8268 {\r
8269   static QuestionParams *qp;\r
8270   char reply[MSG_SIZ];\r
8271   int len, err;\r
8272 \r
8273   switch (message) {\r
8274   case WM_INITDIALOG:\r
8275     qp = (QuestionParams *) lParam;\r
8276     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8277     Translate(hDlg, DLG_Question);\r
8278     SetWindowText(hDlg, qp->title);\r
8279     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8280     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8281     return FALSE;\r
8282 \r
8283   case WM_COMMAND:\r
8284     switch (LOWORD(wParam)) {\r
8285     case IDOK:\r
8286       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8287       if (*reply) strcat(reply, " ");\r
8288       len = strlen(reply);\r
8289       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8290       strcat(reply, "\n");\r
8291       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8292       EndDialog(hDlg, TRUE);\r
8293       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8294       return TRUE;\r
8295     case IDCANCEL:\r
8296       EndDialog(hDlg, FALSE);\r
8297       return TRUE;\r
8298     default:\r
8299       break;\r
8300     }\r
8301     break;\r
8302   }\r
8303   return FALSE;\r
8304 }\r
8305 \r
8306 VOID\r
8307 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8308 {\r
8309     QuestionParams qp;\r
8310     FARPROC lpProc;\r
8311     \r
8312     qp.title = title;\r
8313     qp.question = question;\r
8314     qp.replyPrefix = replyPrefix;\r
8315     qp.pr = pr;\r
8316     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8317     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8318       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8319     FreeProcInstance(lpProc);\r
8320 }\r
8321 \r
8322 /* [AS] Pick FRC position */\r
8323 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8324 {\r
8325     static int * lpIndexFRC;\r
8326     BOOL index_is_ok;\r
8327     char buf[16];\r
8328 \r
8329     switch( message )\r
8330     {\r
8331     case WM_INITDIALOG:\r
8332         lpIndexFRC = (int *) lParam;\r
8333 \r
8334         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8335         Translate(hDlg, DLG_NewGameFRC);\r
8336 \r
8337         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8338         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8339         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8340         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8341 \r
8342         break;\r
8343 \r
8344     case WM_COMMAND:\r
8345         switch( LOWORD(wParam) ) {\r
8346         case IDOK:\r
8347             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8348             EndDialog( hDlg, 0 );\r
8349             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8350             return TRUE;\r
8351         case IDCANCEL:\r
8352             EndDialog( hDlg, 1 );   \r
8353             return TRUE;\r
8354         case IDC_NFG_Edit:\r
8355             if( HIWORD(wParam) == EN_CHANGE ) {\r
8356                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8357 \r
8358                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8359             }\r
8360             return TRUE;\r
8361         case IDC_NFG_Random:\r
8362           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8363             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8364             return TRUE;\r
8365         }\r
8366 \r
8367         break;\r
8368     }\r
8369 \r
8370     return FALSE;\r
8371 }\r
8372 \r
8373 int NewGameFRC()\r
8374 {\r
8375     int result;\r
8376     int index = appData.defaultFrcPosition;\r
8377     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8378 \r
8379     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8380 \r
8381     if( result == 0 ) {\r
8382         appData.defaultFrcPosition = index;\r
8383     }\r
8384 \r
8385     return result;\r
8386 }\r
8387 \r
8388 /* [AS] Game list options. Refactored by HGM */\r
8389 \r
8390 HWND gameListOptionsDialog;\r
8391 \r
8392 // low-level front-end: clear text edit / list widget\r
8393 void\r
8394 GLT_ClearList()\r
8395 {\r
8396     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8397 }\r
8398 \r
8399 // low-level front-end: clear text edit / list widget\r
8400 void\r
8401 GLT_DeSelectList()\r
8402 {\r
8403     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8404 }\r
8405 \r
8406 // low-level front-end: append line to text edit / list widget\r
8407 void\r
8408 GLT_AddToList( char *name )\r
8409 {\r
8410     if( name != 0 ) {\r
8411             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8412     }\r
8413 }\r
8414 \r
8415 // low-level front-end: get line from text edit / list widget\r
8416 Boolean\r
8417 GLT_GetFromList( int index, char *name )\r
8418 {\r
8419     if( name != 0 ) {\r
8420             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8421                 return TRUE;\r
8422     }\r
8423     return FALSE;\r
8424 }\r
8425 \r
8426 void GLT_MoveSelection( HWND hDlg, int delta )\r
8427 {\r
8428     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8429     int idx2 = idx1 + delta;\r
8430     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8431 \r
8432     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8433         char buf[128];\r
8434 \r
8435         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8436         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8437         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8438         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8439     }\r
8440 }\r
8441 \r
8442 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8443 {\r
8444     switch( message )\r
8445     {\r
8446     case WM_INITDIALOG:\r
8447         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8448         \r
8449         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8450         Translate(hDlg, DLG_GameListOptions);\r
8451 \r
8452         /* Initialize list */\r
8453         GLT_TagsToList( lpUserGLT );\r
8454 \r
8455         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8456 \r
8457         break;\r
8458 \r
8459     case WM_COMMAND:\r
8460         switch( LOWORD(wParam) ) {\r
8461         case IDOK:\r
8462             GLT_ParseList();\r
8463             EndDialog( hDlg, 0 );\r
8464             return TRUE;\r
8465         case IDCANCEL:\r
8466             EndDialog( hDlg, 1 );\r
8467             return TRUE;\r
8468 \r
8469         case IDC_GLT_Default:\r
8470             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8471             return TRUE;\r
8472 \r
8473         case IDC_GLT_Restore:\r
8474             GLT_TagsToList( appData.gameListTags );\r
8475             return TRUE;\r
8476 \r
8477         case IDC_GLT_Up:\r
8478             GLT_MoveSelection( hDlg, -1 );\r
8479             return TRUE;\r
8480 \r
8481         case IDC_GLT_Down:\r
8482             GLT_MoveSelection( hDlg, +1 );\r
8483             return TRUE;\r
8484         }\r
8485 \r
8486         break;\r
8487     }\r
8488 \r
8489     return FALSE;\r
8490 }\r
8491 \r
8492 int GameListOptions()\r
8493 {\r
8494     int result;\r
8495     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8496 \r
8497       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8498 \r
8499     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8500 \r
8501     if( result == 0 ) {\r
8502         /* [AS] Memory leak here! */\r
8503         appData.gameListTags = strdup( lpUserGLT ); \r
8504     }\r
8505 \r
8506     return result;\r
8507 }\r
8508 \r
8509 VOID\r
8510 DisplayIcsInteractionTitle(char *str)\r
8511 {\r
8512   char consoleTitle[MSG_SIZ];\r
8513 \r
8514     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8515   SetWindowText(hwndConsole, consoleTitle);\r
8516 }\r
8517 \r
8518 void\r
8519 DrawPosition(int fullRedraw, Board board)\r
8520 {\r
8521   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8522 }\r
8523 \r
8524 void NotifyFrontendLogin()\r
8525 {\r
8526         if (hwndConsole)\r
8527                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8528 }\r
8529 \r
8530 VOID\r
8531 ResetFrontEnd()\r
8532 {\r
8533   fromX = fromY = -1;\r
8534   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8535     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8536     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8537     dragInfo.lastpos = dragInfo.pos;\r
8538     dragInfo.start.x = dragInfo.start.y = -1;\r
8539     dragInfo.from = dragInfo.start;\r
8540     ReleaseCapture();\r
8541     DrawPosition(TRUE, NULL);\r
8542   }\r
8543   TagsPopDown();\r
8544 }\r
8545 \r
8546 \r
8547 VOID\r
8548 CommentPopUp(char *title, char *str)\r
8549 {\r
8550   HWND hwnd = GetActiveWindow();\r
8551   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8552   SAY(str);\r
8553   SetActiveWindow(hwnd);\r
8554 }\r
8555 \r
8556 VOID\r
8557 CommentPopDown(void)\r
8558 {\r
8559   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8560   if (commentDialog) {\r
8561     ShowWindow(commentDialog, SW_HIDE);\r
8562   }\r
8563   commentUp = FALSE;\r
8564 }\r
8565 \r
8566 VOID\r
8567 EditCommentPopUp(int index, char *title, char *str)\r
8568 {\r
8569   EitherCommentPopUp(index, title, str, TRUE);\r
8570 }\r
8571 \r
8572 \r
8573 VOID\r
8574 RingBell()\r
8575 {\r
8576   MyPlaySound(&sounds[(int)SoundMove]);\r
8577 }\r
8578 \r
8579 VOID PlayIcsWinSound()\r
8580 {\r
8581   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8582 }\r
8583 \r
8584 VOID PlayIcsLossSound()\r
8585 {\r
8586   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8587 }\r
8588 \r
8589 VOID PlayIcsDrawSound()\r
8590 {\r
8591   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8592 }\r
8593 \r
8594 VOID PlayIcsUnfinishedSound()\r
8595 {\r
8596   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8597 }\r
8598 \r
8599 VOID\r
8600 PlayAlarmSound()\r
8601 {\r
8602   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8603 }\r
8604 \r
8605 \r
8606 VOID\r
8607 EchoOn()\r
8608 {\r
8609   HWND hInput;\r
8610   consoleEcho = TRUE;\r
8611   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8612   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8613   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8614 }\r
8615 \r
8616 \r
8617 VOID\r
8618 EchoOff()\r
8619 {\r
8620   CHARFORMAT cf;\r
8621   HWND hInput;\r
8622   consoleEcho = FALSE;\r
8623   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8624   /* This works OK: set text and background both to the same color */\r
8625   cf = consoleCF;\r
8626   cf.crTextColor = COLOR_ECHOOFF;\r
8627   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8628   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8629 }\r
8630 \r
8631 /* No Raw()...? */\r
8632 \r
8633 void Colorize(ColorClass cc, int continuation)\r
8634 {\r
8635   currentColorClass = cc;\r
8636   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8637   consoleCF.crTextColor = textAttribs[cc].color;\r
8638   consoleCF.dwEffects = textAttribs[cc].effects;\r
8639   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8640 }\r
8641 \r
8642 char *\r
8643 UserName()\r
8644 {\r
8645   static char buf[MSG_SIZ];\r
8646   DWORD bufsiz = MSG_SIZ;\r
8647 \r
8648   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8649         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8650   }\r
8651   if (!GetUserName(buf, &bufsiz)) {\r
8652     /*DisplayError("Error getting user name", GetLastError());*/\r
8653     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8654   }\r
8655   return buf;\r
8656 }\r
8657 \r
8658 char *\r
8659 HostName()\r
8660 {\r
8661   static char buf[MSG_SIZ];\r
8662   DWORD bufsiz = MSG_SIZ;\r
8663 \r
8664   if (!GetComputerName(buf, &bufsiz)) {\r
8665     /*DisplayError("Error getting host name", GetLastError());*/\r
8666     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8667   }\r
8668   return buf;\r
8669 }\r
8670 \r
8671 \r
8672 int\r
8673 ClockTimerRunning()\r
8674 {\r
8675   return clockTimerEvent != 0;\r
8676 }\r
8677 \r
8678 int\r
8679 StopClockTimer()\r
8680 {\r
8681   if (clockTimerEvent == 0) return FALSE;\r
8682   KillTimer(hwndMain, clockTimerEvent);\r
8683   clockTimerEvent = 0;\r
8684   return TRUE;\r
8685 }\r
8686 \r
8687 void\r
8688 StartClockTimer(long millisec)\r
8689 {\r
8690   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8691                              (UINT) millisec, NULL);\r
8692 }\r
8693 \r
8694 void\r
8695 DisplayWhiteClock(long timeRemaining, int highlight)\r
8696 {\r
8697   HDC hdc;\r
8698   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8699 \r
8700   if(appData.noGUI) return;\r
8701   hdc = GetDC(hwndMain);\r
8702   if (!IsIconic(hwndMain)) {\r
8703     DisplayAClock(hdc, timeRemaining, highlight, \r
8704                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8705   }\r
8706   if (highlight && iconCurrent == iconBlack) {\r
8707     iconCurrent = iconWhite;\r
8708     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8709     if (IsIconic(hwndMain)) {\r
8710       DrawIcon(hdc, 2, 2, iconCurrent);\r
8711     }\r
8712   }\r
8713   (void) ReleaseDC(hwndMain, hdc);\r
8714   if (hwndConsole)\r
8715     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8716 }\r
8717 \r
8718 void\r
8719 DisplayBlackClock(long timeRemaining, int highlight)\r
8720 {\r
8721   HDC hdc;\r
8722   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8723 \r
8724   if(appData.noGUI) return;\r
8725   hdc = GetDC(hwndMain);\r
8726   if (!IsIconic(hwndMain)) {\r
8727     DisplayAClock(hdc, timeRemaining, highlight, \r
8728                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8729   }\r
8730   if (highlight && iconCurrent == iconWhite) {\r
8731     iconCurrent = iconBlack;\r
8732     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8733     if (IsIconic(hwndMain)) {\r
8734       DrawIcon(hdc, 2, 2, iconCurrent);\r
8735     }\r
8736   }\r
8737   (void) ReleaseDC(hwndMain, hdc);\r
8738   if (hwndConsole)\r
8739     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8740 }\r
8741 \r
8742 \r
8743 int\r
8744 LoadGameTimerRunning()\r
8745 {\r
8746   return loadGameTimerEvent != 0;\r
8747 }\r
8748 \r
8749 int\r
8750 StopLoadGameTimer()\r
8751 {\r
8752   if (loadGameTimerEvent == 0) return FALSE;\r
8753   KillTimer(hwndMain, loadGameTimerEvent);\r
8754   loadGameTimerEvent = 0;\r
8755   return TRUE;\r
8756 }\r
8757 \r
8758 void\r
8759 StartLoadGameTimer(long millisec)\r
8760 {\r
8761   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8762                                 (UINT) millisec, NULL);\r
8763 }\r
8764 \r
8765 void\r
8766 AutoSaveGame()\r
8767 {\r
8768   char *defName;\r
8769   FILE *f;\r
8770   char fileTitle[MSG_SIZ];\r
8771 \r
8772   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8773   f = OpenFileDialog(hwndMain, "a", defName,\r
8774                      appData.oldSaveStyle ? "gam" : "pgn",\r
8775                      GAME_FILT, \r
8776                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8777   if (f != NULL) {\r
8778     SaveGame(f, 0, "");\r
8779     fclose(f);\r
8780   }\r
8781 }\r
8782 \r
8783 \r
8784 void\r
8785 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8786 {\r
8787   if (delayedTimerEvent != 0) {\r
8788     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8789       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8790     }\r
8791     KillTimer(hwndMain, delayedTimerEvent);\r
8792     delayedTimerEvent = 0;\r
8793     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8794     delayedTimerCallback();\r
8795   }\r
8796   delayedTimerCallback = cb;\r
8797   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8798                                 (UINT) millisec, NULL);\r
8799 }\r
8800 \r
8801 DelayedEventCallback\r
8802 GetDelayedEvent()\r
8803 {\r
8804   if (delayedTimerEvent) {\r
8805     return delayedTimerCallback;\r
8806   } else {\r
8807     return NULL;\r
8808   }\r
8809 }\r
8810 \r
8811 void\r
8812 CancelDelayedEvent()\r
8813 {\r
8814   if (delayedTimerEvent) {\r
8815     KillTimer(hwndMain, delayedTimerEvent);\r
8816     delayedTimerEvent = 0;\r
8817   }\r
8818 }\r
8819 \r
8820 DWORD GetWin32Priority(int nice)\r
8821 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8822 /*\r
8823 REALTIME_PRIORITY_CLASS     0x00000100\r
8824 HIGH_PRIORITY_CLASS         0x00000080\r
8825 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8826 NORMAL_PRIORITY_CLASS       0x00000020\r
8827 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8828 IDLE_PRIORITY_CLASS         0x00000040\r
8829 */\r
8830         if (nice < -15) return 0x00000080;\r
8831         if (nice < 0)   return 0x00008000;\r
8832         if (nice == 0)  return 0x00000020;\r
8833         if (nice < 15)  return 0x00004000;\r
8834         return 0x00000040;\r
8835 }\r
8836 \r
8837 /* Start a child process running the given program.\r
8838    The process's standard output can be read from "from", and its\r
8839    standard input can be written to "to".\r
8840    Exit with fatal error if anything goes wrong.\r
8841    Returns an opaque pointer that can be used to destroy the process\r
8842    later.\r
8843 */\r
8844 int\r
8845 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8846 {\r
8847 #define BUFSIZE 4096\r
8848 \r
8849   HANDLE hChildStdinRd, hChildStdinWr,\r
8850     hChildStdoutRd, hChildStdoutWr;\r
8851   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8852   SECURITY_ATTRIBUTES saAttr;\r
8853   BOOL fSuccess;\r
8854   PROCESS_INFORMATION piProcInfo;\r
8855   STARTUPINFO siStartInfo;\r
8856   ChildProc *cp;\r
8857   char buf[MSG_SIZ];\r
8858   DWORD err;\r
8859 \r
8860   if (appData.debugMode) {\r
8861     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8862   }\r
8863 \r
8864   *pr = NoProc;\r
8865 \r
8866   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8867   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8868   saAttr.bInheritHandle = TRUE;\r
8869   saAttr.lpSecurityDescriptor = NULL;\r
8870 \r
8871   /*\r
8872    * The steps for redirecting child's STDOUT:\r
8873    *     1. Create anonymous pipe to be STDOUT for child.\r
8874    *     2. Create a noninheritable duplicate of read handle,\r
8875    *         and close the inheritable read handle.\r
8876    */\r
8877 \r
8878   /* Create a pipe for the child's STDOUT. */\r
8879   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8880     return GetLastError();\r
8881   }\r
8882 \r
8883   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8884   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8885                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8886                              FALSE,     /* not inherited */\r
8887                              DUPLICATE_SAME_ACCESS);\r
8888   if (! fSuccess) {\r
8889     return GetLastError();\r
8890   }\r
8891   CloseHandle(hChildStdoutRd);\r
8892 \r
8893   /*\r
8894    * The steps for redirecting child's STDIN:\r
8895    *     1. Create anonymous pipe to be STDIN for child.\r
8896    *     2. Create a noninheritable duplicate of write handle,\r
8897    *         and close the inheritable write handle.\r
8898    */\r
8899 \r
8900   /* Create a pipe for the child's STDIN. */\r
8901   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8902     return GetLastError();\r
8903   }\r
8904 \r
8905   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8906   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8907                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8908                              FALSE,     /* not inherited */\r
8909                              DUPLICATE_SAME_ACCESS);\r
8910   if (! fSuccess) {\r
8911     return GetLastError();\r
8912   }\r
8913   CloseHandle(hChildStdinWr);\r
8914 \r
8915   /* Arrange to (1) look in dir for the child .exe file, and\r
8916    * (2) have dir be the child's working directory.  Interpret\r
8917    * dir relative to the directory WinBoard loaded from. */\r
8918   GetCurrentDirectory(MSG_SIZ, buf);\r
8919   SetCurrentDirectory(installDir);\r
8920   SetCurrentDirectory(dir);\r
8921 \r
8922   /* Now create the child process. */\r
8923 \r
8924   siStartInfo.cb = sizeof(STARTUPINFO);\r
8925   siStartInfo.lpReserved = NULL;\r
8926   siStartInfo.lpDesktop = NULL;\r
8927   siStartInfo.lpTitle = NULL;\r
8928   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8929   siStartInfo.cbReserved2 = 0;\r
8930   siStartInfo.lpReserved2 = NULL;\r
8931   siStartInfo.hStdInput = hChildStdinRd;\r
8932   siStartInfo.hStdOutput = hChildStdoutWr;\r
8933   siStartInfo.hStdError = hChildStdoutWr;\r
8934 \r
8935   fSuccess = CreateProcess(NULL,\r
8936                            cmdLine,        /* command line */\r
8937                            NULL,           /* process security attributes */\r
8938                            NULL,           /* primary thread security attrs */\r
8939                            TRUE,           /* handles are inherited */\r
8940                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8941                            NULL,           /* use parent's environment */\r
8942                            NULL,\r
8943                            &siStartInfo, /* STARTUPINFO pointer */\r
8944                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
8945 \r
8946   err = GetLastError();\r
8947   SetCurrentDirectory(buf); /* return to prev directory */\r
8948   if (! fSuccess) {\r
8949     return err;\r
8950   }\r
8951 \r
8952   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
8953     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
8954     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
8955   }\r
8956 \r
8957   /* Close the handles we don't need in the parent */\r
8958   CloseHandle(piProcInfo.hThread);\r
8959   CloseHandle(hChildStdinRd);\r
8960   CloseHandle(hChildStdoutWr);\r
8961 \r
8962   /* Prepare return value */\r
8963   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8964   cp->kind = CPReal;\r
8965   cp->hProcess = piProcInfo.hProcess;\r
8966   cp->pid = piProcInfo.dwProcessId;\r
8967   cp->hFrom = hChildStdoutRdDup;\r
8968   cp->hTo = hChildStdinWrDup;\r
8969 \r
8970   *pr = (void *) cp;\r
8971 \r
8972   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
8973      2000 where engines sometimes don't see the initial command(s)\r
8974      from WinBoard and hang.  I don't understand how that can happen,\r
8975      but the Sleep is harmless, so I've put it in.  Others have also\r
8976      reported what may be the same problem, so hopefully this will fix\r
8977      it for them too.  */\r
8978   Sleep(500);\r
8979 \r
8980   return NO_ERROR;\r
8981 }\r
8982 \r
8983 \r
8984 void\r
8985 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
8986 {\r
8987   ChildProc *cp; int result;\r
8988 \r
8989   cp = (ChildProc *) pr;\r
8990   if (cp == NULL) return;\r
8991 \r
8992   switch (cp->kind) {\r
8993   case CPReal:\r
8994     /* TerminateProcess is considered harmful, so... */\r
8995     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
8996     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
8997     /* The following doesn't work because the chess program\r
8998        doesn't "have the same console" as WinBoard.  Maybe\r
8999        we could arrange for this even though neither WinBoard\r
9000        nor the chess program uses a console for stdio? */\r
9001     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9002 \r
9003     /* [AS] Special termination modes for misbehaving programs... */\r
9004     if( signal == 9 ) { \r
9005         result = TerminateProcess( cp->hProcess, 0 );\r
9006 \r
9007         if ( appData.debugMode) {\r
9008             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9009         }\r
9010     }\r
9011     else if( signal == 10 ) {\r
9012         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9013 \r
9014         if( dw != WAIT_OBJECT_0 ) {\r
9015             result = TerminateProcess( cp->hProcess, 0 );\r
9016 \r
9017             if ( appData.debugMode) {\r
9018                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9019             }\r
9020 \r
9021         }\r
9022     }\r
9023 \r
9024     CloseHandle(cp->hProcess);\r
9025     break;\r
9026 \r
9027   case CPComm:\r
9028     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9029     break;\r
9030 \r
9031   case CPSock:\r
9032     closesocket(cp->sock);\r
9033     WSACleanup();\r
9034     break;\r
9035 \r
9036   case CPRcmd:\r
9037     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9038     closesocket(cp->sock);\r
9039     closesocket(cp->sock2);\r
9040     WSACleanup();\r
9041     break;\r
9042   }\r
9043   free(cp);\r
9044 }\r
9045 \r
9046 void\r
9047 InterruptChildProcess(ProcRef pr)\r
9048 {\r
9049   ChildProc *cp;\r
9050 \r
9051   cp = (ChildProc *) pr;\r
9052   if (cp == NULL) return;\r
9053   switch (cp->kind) {\r
9054   case CPReal:\r
9055     /* The following doesn't work because the chess program\r
9056        doesn't "have the same console" as WinBoard.  Maybe\r
9057        we could arrange for this even though neither WinBoard\r
9058        nor the chess program uses a console for stdio */\r
9059     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9060     break;\r
9061 \r
9062   case CPComm:\r
9063   case CPSock:\r
9064     /* Can't interrupt */\r
9065     break;\r
9066 \r
9067   case CPRcmd:\r
9068     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9069     break;\r
9070   }\r
9071 }\r
9072 \r
9073 \r
9074 int\r
9075 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9076 {\r
9077   char cmdLine[MSG_SIZ];\r
9078 \r
9079   if (port[0] == NULLCHAR) {\r
9080     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9081   } else {\r
9082     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9083   }\r
9084   return StartChildProcess(cmdLine, "", pr);\r
9085 }\r
9086 \r
9087 \r
9088 /* Code to open TCP sockets */\r
9089 \r
9090 int\r
9091 OpenTCP(char *host, char *port, ProcRef *pr)\r
9092 {\r
9093   ChildProc *cp;\r
9094   int err;\r
9095   SOCKET s;\r
9096   struct sockaddr_in sa, mysa;\r
9097   struct hostent FAR *hp;\r
9098   unsigned short uport;\r
9099   WORD wVersionRequested;\r
9100   WSADATA wsaData;\r
9101 \r
9102   /* Initialize socket DLL */\r
9103   wVersionRequested = MAKEWORD(1, 1);\r
9104   err = WSAStartup(wVersionRequested, &wsaData);\r
9105   if (err != 0) return err;\r
9106 \r
9107   /* Make socket */\r
9108   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9109     err = WSAGetLastError();\r
9110     WSACleanup();\r
9111     return err;\r
9112   }\r
9113 \r
9114   /* Bind local address using (mostly) don't-care values.\r
9115    */\r
9116   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9117   mysa.sin_family = AF_INET;\r
9118   mysa.sin_addr.s_addr = INADDR_ANY;\r
9119   uport = (unsigned short) 0;\r
9120   mysa.sin_port = htons(uport);\r
9121   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9122       == SOCKET_ERROR) {\r
9123     err = WSAGetLastError();\r
9124     WSACleanup();\r
9125     return err;\r
9126   }\r
9127 \r
9128   /* Resolve remote host name */\r
9129   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9130   if (!(hp = gethostbyname(host))) {\r
9131     unsigned int b0, b1, b2, b3;\r
9132 \r
9133     err = WSAGetLastError();\r
9134 \r
9135     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9136       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9137       hp->h_addrtype = AF_INET;\r
9138       hp->h_length = 4;\r
9139       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9140       hp->h_addr_list[0] = (char *) malloc(4);\r
9141       hp->h_addr_list[0][0] = (char) b0;\r
9142       hp->h_addr_list[0][1] = (char) b1;\r
9143       hp->h_addr_list[0][2] = (char) b2;\r
9144       hp->h_addr_list[0][3] = (char) b3;\r
9145     } else {\r
9146       WSACleanup();\r
9147       return err;\r
9148     }\r
9149   }\r
9150   sa.sin_family = hp->h_addrtype;\r
9151   uport = (unsigned short) atoi(port);\r
9152   sa.sin_port = htons(uport);\r
9153   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9154 \r
9155   /* Make connection */\r
9156   if (connect(s, (struct sockaddr *) &sa,\r
9157               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9158     err = WSAGetLastError();\r
9159     WSACleanup();\r
9160     return err;\r
9161   }\r
9162 \r
9163   /* Prepare return value */\r
9164   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9165   cp->kind = CPSock;\r
9166   cp->sock = s;\r
9167   *pr = (ProcRef *) cp;\r
9168 \r
9169   return NO_ERROR;\r
9170 }\r
9171 \r
9172 int\r
9173 OpenCommPort(char *name, ProcRef *pr)\r
9174 {\r
9175   HANDLE h;\r
9176   COMMTIMEOUTS ct;\r
9177   ChildProc *cp;\r
9178   char fullname[MSG_SIZ];\r
9179 \r
9180   if (*name != '\\')\r
9181     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9182   else\r
9183     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9184 \r
9185   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9186                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9187   if (h == (HANDLE) -1) {\r
9188     return GetLastError();\r
9189   }\r
9190   hCommPort = h;\r
9191 \r
9192   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9193 \r
9194   /* Accumulate characters until a 100ms pause, then parse */\r
9195   ct.ReadIntervalTimeout = 100;\r
9196   ct.ReadTotalTimeoutMultiplier = 0;\r
9197   ct.ReadTotalTimeoutConstant = 0;\r
9198   ct.WriteTotalTimeoutMultiplier = 0;\r
9199   ct.WriteTotalTimeoutConstant = 0;\r
9200   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9201 \r
9202   /* Prepare return value */\r
9203   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9204   cp->kind = CPComm;\r
9205   cp->hFrom = h;\r
9206   cp->hTo = h;\r
9207   *pr = (ProcRef *) cp;\r
9208 \r
9209   return NO_ERROR;\r
9210 }\r
9211 \r
9212 int\r
9213 OpenLoopback(ProcRef *pr)\r
9214 {\r
9215   DisplayFatalError(_("Not implemented"), 0, 1);\r
9216   return NO_ERROR;\r
9217 }\r
9218 \r
9219 \r
9220 int\r
9221 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9222 {\r
9223   ChildProc *cp;\r
9224   int err;\r
9225   SOCKET s, s2, s3;\r
9226   struct sockaddr_in sa, mysa;\r
9227   struct hostent FAR *hp;\r
9228   unsigned short uport;\r
9229   WORD wVersionRequested;\r
9230   WSADATA wsaData;\r
9231   int fromPort;\r
9232   char stderrPortStr[MSG_SIZ];\r
9233 \r
9234   /* Initialize socket DLL */\r
9235   wVersionRequested = MAKEWORD(1, 1);\r
9236   err = WSAStartup(wVersionRequested, &wsaData);\r
9237   if (err != 0) return err;\r
9238 \r
9239   /* Resolve remote host name */\r
9240   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9241   if (!(hp = gethostbyname(host))) {\r
9242     unsigned int b0, b1, b2, b3;\r
9243 \r
9244     err = WSAGetLastError();\r
9245 \r
9246     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9247       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9248       hp->h_addrtype = AF_INET;\r
9249       hp->h_length = 4;\r
9250       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9251       hp->h_addr_list[0] = (char *) malloc(4);\r
9252       hp->h_addr_list[0][0] = (char) b0;\r
9253       hp->h_addr_list[0][1] = (char) b1;\r
9254       hp->h_addr_list[0][2] = (char) b2;\r
9255       hp->h_addr_list[0][3] = (char) b3;\r
9256     } else {\r
9257       WSACleanup();\r
9258       return err;\r
9259     }\r
9260   }\r
9261   sa.sin_family = hp->h_addrtype;\r
9262   uport = (unsigned short) 514;\r
9263   sa.sin_port = htons(uport);\r
9264   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9265 \r
9266   /* Bind local socket to unused "privileged" port address\r
9267    */\r
9268   s = INVALID_SOCKET;\r
9269   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9270   mysa.sin_family = AF_INET;\r
9271   mysa.sin_addr.s_addr = INADDR_ANY;\r
9272   for (fromPort = 1023;; fromPort--) {\r
9273     if (fromPort < 0) {\r
9274       WSACleanup();\r
9275       return WSAEADDRINUSE;\r
9276     }\r
9277     if (s == INVALID_SOCKET) {\r
9278       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9279         err = WSAGetLastError();\r
9280         WSACleanup();\r
9281         return err;\r
9282       }\r
9283     }\r
9284     uport = (unsigned short) fromPort;\r
9285     mysa.sin_port = htons(uport);\r
9286     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9287         == SOCKET_ERROR) {\r
9288       err = WSAGetLastError();\r
9289       if (err == WSAEADDRINUSE) continue;\r
9290       WSACleanup();\r
9291       return err;\r
9292     }\r
9293     if (connect(s, (struct sockaddr *) &sa,\r
9294       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9295       err = WSAGetLastError();\r
9296       if (err == WSAEADDRINUSE) {\r
9297         closesocket(s);\r
9298         s = -1;\r
9299         continue;\r
9300       }\r
9301       WSACleanup();\r
9302       return err;\r
9303     }\r
9304     break;\r
9305   }\r
9306 \r
9307   /* Bind stderr local socket to unused "privileged" port address\r
9308    */\r
9309   s2 = INVALID_SOCKET;\r
9310   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9311   mysa.sin_family = AF_INET;\r
9312   mysa.sin_addr.s_addr = INADDR_ANY;\r
9313   for (fromPort = 1023;; fromPort--) {\r
9314     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9315     if (fromPort < 0) {\r
9316       (void) closesocket(s);\r
9317       WSACleanup();\r
9318       return WSAEADDRINUSE;\r
9319     }\r
9320     if (s2 == INVALID_SOCKET) {\r
9321       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9322         err = WSAGetLastError();\r
9323         closesocket(s);\r
9324         WSACleanup();\r
9325         return err;\r
9326       }\r
9327     }\r
9328     uport = (unsigned short) fromPort;\r
9329     mysa.sin_port = htons(uport);\r
9330     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9331         == SOCKET_ERROR) {\r
9332       err = WSAGetLastError();\r
9333       if (err == WSAEADDRINUSE) continue;\r
9334       (void) closesocket(s);\r
9335       WSACleanup();\r
9336       return err;\r
9337     }\r
9338     if (listen(s2, 1) == SOCKET_ERROR) {\r
9339       err = WSAGetLastError();\r
9340       if (err == WSAEADDRINUSE) {\r
9341         closesocket(s2);\r
9342         s2 = INVALID_SOCKET;\r
9343         continue;\r
9344       }\r
9345       (void) closesocket(s);\r
9346       (void) closesocket(s2);\r
9347       WSACleanup();\r
9348       return err;\r
9349     }\r
9350     break;\r
9351   }\r
9352   prevStderrPort = fromPort; // remember port used\r
9353   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9354 \r
9355   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9356     err = WSAGetLastError();\r
9357     (void) closesocket(s);\r
9358     (void) closesocket(s2);\r
9359     WSACleanup();\r
9360     return err;\r
9361   }\r
9362 \r
9363   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9364     err = WSAGetLastError();\r
9365     (void) closesocket(s);\r
9366     (void) closesocket(s2);\r
9367     WSACleanup();\r
9368     return err;\r
9369   }\r
9370   if (*user == NULLCHAR) user = UserName();\r
9371   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9372     err = WSAGetLastError();\r
9373     (void) closesocket(s);\r
9374     (void) closesocket(s2);\r
9375     WSACleanup();\r
9376     return err;\r
9377   }\r
9378   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9379     err = WSAGetLastError();\r
9380     (void) closesocket(s);\r
9381     (void) closesocket(s2);\r
9382     WSACleanup();\r
9383     return err;\r
9384   }\r
9385 \r
9386   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9387     err = WSAGetLastError();\r
9388     (void) closesocket(s);\r
9389     (void) closesocket(s2);\r
9390     WSACleanup();\r
9391     return err;\r
9392   }\r
9393   (void) closesocket(s2);  /* Stop listening */\r
9394 \r
9395   /* Prepare return value */\r
9396   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9397   cp->kind = CPRcmd;\r
9398   cp->sock = s;\r
9399   cp->sock2 = s3;\r
9400   *pr = (ProcRef *) cp;\r
9401 \r
9402   return NO_ERROR;\r
9403 }\r
9404 \r
9405 \r
9406 InputSourceRef\r
9407 AddInputSource(ProcRef pr, int lineByLine,\r
9408                InputCallback func, VOIDSTAR closure)\r
9409 {\r
9410   InputSource *is, *is2 = NULL;\r
9411   ChildProc *cp = (ChildProc *) pr;\r
9412 \r
9413   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9414   is->lineByLine = lineByLine;\r
9415   is->func = func;\r
9416   is->closure = closure;\r
9417   is->second = NULL;\r
9418   is->next = is->buf;\r
9419   if (pr == NoProc) {\r
9420     is->kind = CPReal;\r
9421     consoleInputSource = is;\r
9422   } else {\r
9423     is->kind = cp->kind;\r
9424     /* \r
9425         [AS] Try to avoid a race condition if the thread is given control too early:\r
9426         we create all threads suspended so that the is->hThread variable can be\r
9427         safely assigned, then let the threads start with ResumeThread.\r
9428     */\r
9429     switch (cp->kind) {\r
9430     case CPReal:\r
9431       is->hFile = cp->hFrom;\r
9432       cp->hFrom = NULL; /* now owned by InputThread */\r
9433       is->hThread =\r
9434         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9435                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9436       break;\r
9437 \r
9438     case CPComm:\r
9439       is->hFile = cp->hFrom;\r
9440       cp->hFrom = NULL; /* now owned by InputThread */\r
9441       is->hThread =\r
9442         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9443                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9444       break;\r
9445 \r
9446     case CPSock:\r
9447       is->sock = cp->sock;\r
9448       is->hThread =\r
9449         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9450                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9451       break;\r
9452 \r
9453     case CPRcmd:\r
9454       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9455       *is2 = *is;\r
9456       is->sock = cp->sock;\r
9457       is->second = is2;\r
9458       is2->sock = cp->sock2;\r
9459       is2->second = is2;\r
9460       is->hThread =\r
9461         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9462                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9463       is2->hThread =\r
9464         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9465                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9466       break;\r
9467     }\r
9468 \r
9469     if( is->hThread != NULL ) {\r
9470         ResumeThread( is->hThread );\r
9471     }\r
9472 \r
9473     if( is2 != NULL && is2->hThread != NULL ) {\r
9474         ResumeThread( is2->hThread );\r
9475     }\r
9476   }\r
9477 \r
9478   return (InputSourceRef) is;\r
9479 }\r
9480 \r
9481 void\r
9482 RemoveInputSource(InputSourceRef isr)\r
9483 {\r
9484   InputSource *is;\r
9485 \r
9486   is = (InputSource *) isr;\r
9487   is->hThread = NULL;  /* tell thread to stop */\r
9488   CloseHandle(is->hThread);\r
9489   if (is->second != NULL) {\r
9490     is->second->hThread = NULL;\r
9491     CloseHandle(is->second->hThread);\r
9492   }\r
9493 }\r
9494 \r
9495 int no_wrap(char *message, int count)\r
9496 {\r
9497     ConsoleOutput(message, count, FALSE);\r
9498     return count;\r
9499 }\r
9500 \r
9501 int\r
9502 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9503 {\r
9504   DWORD dOutCount;\r
9505   int outCount = SOCKET_ERROR;\r
9506   ChildProc *cp = (ChildProc *) pr;\r
9507   static OVERLAPPED ovl;\r
9508   static int line = 0;\r
9509 \r
9510   if (pr == NoProc)\r
9511   {\r
9512     if (appData.noJoin || !appData.useInternalWrap)\r
9513       return no_wrap(message, count);\r
9514     else\r
9515     {\r
9516       int width = get_term_width();\r
9517       int len = wrap(NULL, message, count, width, &line);\r
9518       char *msg = malloc(len);\r
9519       int dbgchk;\r
9520 \r
9521       if (!msg)\r
9522         return no_wrap(message, count);\r
9523       else\r
9524       {\r
9525         dbgchk = wrap(msg, message, count, width, &line);\r
9526         if (dbgchk != len && appData.debugMode)\r
9527             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9528         ConsoleOutput(msg, len, FALSE);\r
9529         free(msg);\r
9530         return len;\r
9531       }\r
9532     }\r
9533   }\r
9534 \r
9535   if (ovl.hEvent == NULL) {\r
9536     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9537   }\r
9538   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9539 \r
9540   switch (cp->kind) {\r
9541   case CPSock:\r
9542   case CPRcmd:\r
9543     outCount = send(cp->sock, message, count, 0);\r
9544     if (outCount == SOCKET_ERROR) {\r
9545       *outError = WSAGetLastError();\r
9546     } else {\r
9547       *outError = NO_ERROR;\r
9548     }\r
9549     break;\r
9550 \r
9551   case CPReal:\r
9552     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9553                   &dOutCount, NULL)) {\r
9554       *outError = NO_ERROR;\r
9555       outCount = (int) dOutCount;\r
9556     } else {\r
9557       *outError = GetLastError();\r
9558     }\r
9559     break;\r
9560 \r
9561   case CPComm:\r
9562     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9563                             &dOutCount, &ovl);\r
9564     if (*outError == NO_ERROR) {\r
9565       outCount = (int) dOutCount;\r
9566     }\r
9567     break;\r
9568   }\r
9569   return outCount;\r
9570 }\r
9571 \r
9572 int\r
9573 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9574                        long msdelay)\r
9575 {\r
9576   /* Ignore delay, not implemented for WinBoard */\r
9577   return OutputToProcess(pr, message, count, outError);\r
9578 }\r
9579 \r
9580 \r
9581 void\r
9582 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9583                         char *buf, int count, int error)\r
9584 {\r
9585   DisplayFatalError(_("Not implemented"), 0, 1);\r
9586 }\r
9587 \r
9588 /* see wgamelist.c for Game List functions */\r
9589 /* see wedittags.c for Edit Tags functions */\r
9590 \r
9591 \r
9592 VOID\r
9593 ICSInitScript()\r
9594 {\r
9595   FILE *f;\r
9596   char buf[MSG_SIZ];\r
9597   char *dummy;\r
9598 \r
9599   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9600     f = fopen(buf, "r");\r
9601     if (f != NULL) {\r
9602       ProcessICSInitScript(f);\r
9603       fclose(f);\r
9604     }\r
9605   }\r
9606 }\r
9607 \r
9608 \r
9609 VOID\r
9610 StartAnalysisClock()\r
9611 {\r
9612   if (analysisTimerEvent) return;\r
9613   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9614                                         (UINT) 2000, NULL);\r
9615 }\r
9616 \r
9617 VOID\r
9618 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9619 {\r
9620   highlightInfo.sq[0].x = fromX;\r
9621   highlightInfo.sq[0].y = fromY;\r
9622   highlightInfo.sq[1].x = toX;\r
9623   highlightInfo.sq[1].y = toY;\r
9624 }\r
9625 \r
9626 VOID\r
9627 ClearHighlights()\r
9628 {\r
9629   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9630     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9631 }\r
9632 \r
9633 VOID\r
9634 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9635 {\r
9636   premoveHighlightInfo.sq[0].x = fromX;\r
9637   premoveHighlightInfo.sq[0].y = fromY;\r
9638   premoveHighlightInfo.sq[1].x = toX;\r
9639   premoveHighlightInfo.sq[1].y = toY;\r
9640 }\r
9641 \r
9642 VOID\r
9643 ClearPremoveHighlights()\r
9644 {\r
9645   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9646     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9647 }\r
9648 \r
9649 VOID\r
9650 ShutDownFrontEnd()\r
9651 {\r
9652   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9653   DeleteClipboardTempFiles();\r
9654 }\r
9655 \r
9656 void\r
9657 BoardToTop()\r
9658 {\r
9659     if (IsIconic(hwndMain))\r
9660       ShowWindow(hwndMain, SW_RESTORE);\r
9661 \r
9662     SetActiveWindow(hwndMain);\r
9663 }\r
9664 \r
9665 /*\r
9666  * Prototypes for animation support routines\r
9667  */\r
9668 static void ScreenSquare(int column, int row, POINT * pt);\r
9669 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9670      POINT frames[], int * nFrames);\r
9671 \r
9672 \r
9673 #define kFactor 4\r
9674 \r
9675 void\r
9676 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9677 {       // [HGM] atomic: animate blast wave\r
9678         int i;\r
9679 \r
9680         explodeInfo.fromX = fromX;\r
9681         explodeInfo.fromY = fromY;\r
9682         explodeInfo.toX = toX;\r
9683         explodeInfo.toY = toY;\r
9684         for(i=1; i<4*kFactor; i++) {\r
9685             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9686             DrawPosition(FALSE, board);\r
9687             Sleep(appData.animSpeed);\r
9688         }\r
9689         explodeInfo.radius = 0;\r
9690         DrawPosition(TRUE, board);\r
9691 }\r
9692 \r
9693 void\r
9694 AnimateMove(board, fromX, fromY, toX, toY)\r
9695      Board board;\r
9696      int fromX;\r
9697      int fromY;\r
9698      int toX;\r
9699      int toY;\r
9700 {\r
9701   ChessSquare piece;\r
9702   POINT start, finish, mid;\r
9703   POINT frames[kFactor * 2 + 1];\r
9704   int nFrames, n;\r
9705 \r
9706   if (!appData.animate) return;\r
9707   if (doingSizing) return;\r
9708   if (fromY < 0 || fromX < 0) return;\r
9709   piece = board[fromY][fromX];\r
9710   if (piece >= EmptySquare) return;\r
9711 \r
9712   ScreenSquare(fromX, fromY, &start);\r
9713   ScreenSquare(toX, toY, &finish);\r
9714 \r
9715   /* All moves except knight jumps move in straight line */\r
9716   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9717     mid.x = start.x + (finish.x - start.x) / 2;\r
9718     mid.y = start.y + (finish.y - start.y) / 2;\r
9719   } else {\r
9720     /* Knight: make straight movement then diagonal */\r
9721     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9722        mid.x = start.x + (finish.x - start.x) / 2;\r
9723        mid.y = start.y;\r
9724      } else {\r
9725        mid.x = start.x;\r
9726        mid.y = start.y + (finish.y - start.y) / 2;\r
9727      }\r
9728   }\r
9729   \r
9730   /* Don't use as many frames for very short moves */\r
9731   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9732     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9733   else\r
9734     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9735 \r
9736   animInfo.from.x = fromX;\r
9737   animInfo.from.y = fromY;\r
9738   animInfo.to.x = toX;\r
9739   animInfo.to.y = toY;\r
9740   animInfo.lastpos = start;\r
9741   animInfo.piece = piece;\r
9742   for (n = 0; n < nFrames; n++) {\r
9743     animInfo.pos = frames[n];\r
9744     DrawPosition(FALSE, NULL);\r
9745     animInfo.lastpos = animInfo.pos;\r
9746     Sleep(appData.animSpeed);\r
9747   }\r
9748   animInfo.pos = finish;\r
9749   DrawPosition(FALSE, NULL);\r
9750   animInfo.piece = EmptySquare;\r
9751   Explode(board, fromX, fromY, toX, toY);\r
9752 }\r
9753 \r
9754 /*      Convert board position to corner of screen rect and color       */\r
9755 \r
9756 static void\r
9757 ScreenSquare(column, row, pt)\r
9758      int column; int row; POINT * pt;\r
9759 {\r
9760   if (flipView) {\r
9761     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9762     pt->y = lineGap + row * (squareSize + lineGap);\r
9763   } else {\r
9764     pt->x = lineGap + column * (squareSize + lineGap);\r
9765     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9766   }\r
9767 }\r
9768 \r
9769 /*      Generate a series of frame coords from start->mid->finish.\r
9770         The movement rate doubles until the half way point is\r
9771         reached, then halves back down to the final destination,\r
9772         which gives a nice slow in/out effect. The algorithmn\r
9773         may seem to generate too many intermediates for short\r
9774         moves, but remember that the purpose is to attract the\r
9775         viewers attention to the piece about to be moved and\r
9776         then to where it ends up. Too few frames would be less\r
9777         noticeable.                                             */\r
9778 \r
9779 static void\r
9780 Tween(start, mid, finish, factor, frames, nFrames)\r
9781      POINT * start; POINT * mid;\r
9782      POINT * finish; int factor;\r
9783      POINT frames[]; int * nFrames;\r
9784 {\r
9785   int n, fraction = 1, count = 0;\r
9786 \r
9787   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9788   for (n = 0; n < factor; n++)\r
9789     fraction *= 2;\r
9790   for (n = 0; n < factor; n++) {\r
9791     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9792     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9793     count ++;\r
9794     fraction = fraction / 2;\r
9795   }\r
9796   \r
9797   /* Midpoint */\r
9798   frames[count] = *mid;\r
9799   count ++;\r
9800   \r
9801   /* Slow out, stepping 1/2, then 1/4, ... */\r
9802   fraction = 2;\r
9803   for (n = 0; n < factor; n++) {\r
9804     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9805     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9806     count ++;\r
9807     fraction = fraction * 2;\r
9808   }\r
9809   *nFrames = count;\r
9810 }\r
9811 \r
9812 void\r
9813 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9814 {\r
9815     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9816 \r
9817     EvalGraphSet( first, last, current, pvInfoList );\r
9818 }\r
9819 \r
9820 void\r
9821 SettingsPopUp(ChessProgramState *cps)\r
9822 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9823       EngineOptionsPopup(savedHwnd, cps);\r
9824 }\r