Make language choice from menu persistent
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts. \r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 \r
77 #if __GNUC__\r
78 #include <errno.h>\r
79 #include <string.h>\r
80 #endif\r
81 \r
82 #include "common.h"\r
83 #include "frontend.h"\r
84 #include "backend.h"\r
85 #include "winboard.h"\r
86 #include "moves.h"\r
87 #include "wclipbrd.h"\r
88 #include "woptions.h"\r
89 #include "wsockerr.h"\r
90 #include "defaults.h"\r
91 #include "help.h"\r
92 #include "wsnap.h"\r
93 \r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
95 \r
96   int myrandom(void);\r
97   void mysrandom(unsigned int seed);\r
98 \r
99 extern int whiteFlag, blackFlag;\r
100 Boolean flipClock = FALSE;\r
101 extern HANDLE chatHandle[];\r
102 extern int ics_type;\r
103 \r
104 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
105 VOID NewVariantPopup(HWND hwnd);\r
106 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
107                    /*char*/int promoChar));\r
108 void DisplayMove P((int moveNumber));\r
109 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
110 void ChatPopUp P((char *s));\r
111 typedef struct {\r
112   ChessSquare piece;  \r
113   POINT pos;      /* window coordinates of current pos */\r
114   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
115   POINT from;     /* board coordinates of the piece's orig pos */\r
116   POINT to;       /* board coordinates of the piece's new pos */\r
117 } AnimInfo;\r
118 \r
119 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
120 \r
121 typedef struct {\r
122   POINT start;    /* window coordinates of start pos */\r
123   POINT pos;      /* window coordinates of current pos */\r
124   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
125   POINT from;     /* board coordinates of the piece's orig pos */\r
126 } DragInfo;\r
127 \r
128 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
129 \r
130 typedef struct {\r
131   POINT sq[2];    /* board coordinates of from, to squares */\r
132 } HighlightInfo;\r
133 \r
134 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
135 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
136 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
137 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
138 \r
139 typedef struct { // [HGM] atomic\r
140   int fromX, fromY, toX, toY, radius;\r
141 } ExplodeInfo;\r
142 \r
143 static ExplodeInfo explodeInfo;\r
144 \r
145 /* Window class names */\r
146 char szAppName[] = "WinBoard";\r
147 char szConsoleName[] = "WBConsole";\r
148 \r
149 /* Title bar text */\r
150 char szTitle[] = "WinBoard";\r
151 char szConsoleTitle[] = "I C S Interaction";\r
152 \r
153 char *programName;\r
154 char *settingsFileName;\r
155 Boolean saveSettingsOnExit;\r
156 char installDir[MSG_SIZ];\r
157 int errorExitStatus;\r
158 \r
159 BoardSize boardSize;\r
160 Boolean chessProgram;\r
161 //static int boardX, boardY;\r
162 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
163 int squareSize, lineGap, minorSize;\r
164 static int winW, winH;\r
165 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
166 static int logoHeight = 0;\r
167 static char messageText[MESSAGE_TEXT_MAX];\r
168 static int clockTimerEvent = 0;\r
169 static int loadGameTimerEvent = 0;\r
170 static int analysisTimerEvent = 0;\r
171 static DelayedEventCallback delayedTimerCallback;\r
172 static int delayedTimerEvent = 0;\r
173 static int buttonCount = 2;\r
174 char *icsTextMenuString;\r
175 char *icsNames;\r
176 char *firstChessProgramNames;\r
177 char *secondChessProgramNames;\r
178 \r
179 #define PALETTESIZE 256\r
180 \r
181 HINSTANCE hInst;          /* current instance */\r
182 Boolean alwaysOnTop = FALSE;\r
183 RECT boardRect;\r
184 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
185   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
186 HPALETTE hPal;\r
187 ColorClass currentColorClass;\r
188 \r
189 static HWND savedHwnd;\r
190 HWND hCommPort = NULL;    /* currently open comm port */\r
191 static HWND hwndPause;    /* pause button */\r
192 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
193 static HBRUSH lightSquareBrush, darkSquareBrush,\r
194   blackSquareBrush, /* [HGM] for band between board and holdings */\r
195   explodeBrush,     /* [HGM] atomic */\r
196   markerBrush,      /* [HGM] markers */\r
197   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
198 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
199 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
200 static HPEN gridPen = NULL;\r
201 static HPEN highlightPen = NULL;\r
202 static HPEN premovePen = NULL;\r
203 static NPLOGPALETTE pLogPal;\r
204 static BOOL paletteChanged = FALSE;\r
205 static HICON iconWhite, iconBlack, iconCurrent;\r
206 static int doingSizing = FALSE;\r
207 static int lastSizing = 0;\r
208 static int prevStderrPort;\r
209 static HBITMAP userLogo;\r
210 \r
211 static HBITMAP liteBackTexture = NULL;\r
212 static HBITMAP darkBackTexture = NULL;\r
213 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
214 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
215 static int backTextureSquareSize = 0;\r
216 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
217 \r
218 #if __GNUC__ && !defined(_winmajor)\r
219 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
220 #else\r
221 #if defined(_winmajor)\r
222 #define oldDialog (_winmajor < 4)\r
223 #else\r
224 #define oldDialog 0\r
225 #endif\r
226 #endif\r
227 \r
228 #define INTERNATIONAL\r
229 \r
230 #ifdef INTERNATIONAL\r
231 #  define _(s) T_(s)\r
232 #  define N_(s) s\r
233 #else\r
234 #  define _(s) s\r
235 #  define N_(s) s\r
236 #  define T_(s) s\r
237 #  define Translate(x, y)\r
238 #  define LoadLanguageFile(s)\r
239 #endif\r
240 \r
241 #ifdef INTERNATIONAL\r
242 \r
243 Boolean barbaric; // flag indicating if translation is needed\r
244 \r
245 // list of item numbers used in each dialog (used to alter language at run time)\r
246 \r
247 #define ABOUTBOX -1  /* not sure why these are needed */\r
248 #define ABOUTBOX2 -1\r
249 \r
250 int dialogItems[][40] = {\r
251 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
252 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
253   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
254 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, IDOK, IDCANCEL }, \r
255 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
256   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
257 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
258 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
259   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
260 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
261 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
262   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
263 { ABOUTBOX2, IDC_ChessBoard }, \r
264 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
265   OPT_GameListClose, IDC_GameListDoFilter }, \r
266 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
267 { DLG_Error, IDOK }, \r
268 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
269   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
270 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
271 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
272   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
273   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
274 { DLG_IndexNumber, IDC_Index }, \r
275 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
276 { DLG_TypeInName, IDOK, IDCANCEL }, \r
277 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
278   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
279 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
280   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
281   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
282   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
283   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
284   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
285   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
286 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
287   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
288   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
289   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
290   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
291   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
292   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
293   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
294   GPB_General, GPB_Alarm }, \r
295 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
296   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
297   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
298   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
299   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
300   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
301   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
302   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size }, \r
303 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
304   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
305   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
306   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
307   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
308   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
309   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
310   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
311   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
312 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
313   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
314   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,\r
315   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
316 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
317 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
318   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
319 { DLG_MoveHistory }, \r
320 { DLG_EvalGraph }, \r
321 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
322 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
323 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
324   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
325   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
326   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
327 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
328   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
329   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
330 { 0 }\r
331 };\r
332 \r
333 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
334 static int lastChecked;\r
335 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
336 extern int tinyLayout;\r
337 extern char * menuBarText[][10];\r
338 \r
339 void\r
340 LoadLanguageFile(char *name)\r
341 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
342     FILE *f;\r
343     int i=0, j=0, n=0, k;\r
344     char buf[MSG_SIZ];\r
345 \r
346     if(!name || name[0] == NULLCHAR) return;\r
347       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
348     appData.language = oldLanguage;\r
349     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
350     if((f = fopen(buf, "r")) == NULL) return;\r
351     while((k = fgetc(f)) != EOF) {\r
352         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
353         languageBuf[i] = k;\r
354         if(k == '\n') {\r
355             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
356                 char *p;\r
357                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
358                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
359                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
360                         english[j] = languageBuf + n + 1; *p = 0;\r
361                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
362 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
363                     }\r
364                 }\r
365             }\r
366             n = i + 1;\r
367         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
368             switch(k) {\r
369               case 'n': k = '\n'; break;\r
370               case 'r': k = '\r'; break;\r
371               case 't': k = '\t'; break;\r
372             }\r
373             languageBuf[--i] = k;\r
374         }\r
375         i++;\r
376     }\r
377     fclose(f);\r
378     barbaric = (j != 0);\r
379     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
380 }\r
381 \r
382 char *\r
383 T_(char *s)\r
384 {   // return the translation of the given string\r
385     // efficiency can be improved a lot...\r
386     int i=0;\r
387 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
388     if(!barbaric) return s;\r
389     if(!s) return ""; // sanity\r
390     while(english[i]) {\r
391         if(!strcmp(s, english[i])) return foreign[i];\r
392         i++;\r
393     }\r
394     return s;\r
395 }\r
396 \r
397 void\r
398 Translate(HWND hDlg, int dialogID)\r
399 {   // translate all text items in the given dialog\r
400     int i=0, j, k;\r
401     char buf[MSG_SIZ], *s;\r
402     if(!barbaric) return;\r
403     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
404     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
405     GetWindowText( hDlg, buf, MSG_SIZ );\r
406     s = T_(buf);\r
407     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
408     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
409         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
410         if(strlen(buf) == 0) continue;\r
411         s = T_(buf);\r
412         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
413     }\r
414 }\r
415 \r
416 HMENU\r
417 TranslateOneMenu(int i, HMENU subMenu)\r
418 {\r
419     int j;\r
420     static MENUITEMINFO info;\r
421 \r
422     info.cbSize = sizeof(MENUITEMINFO);\r
423     info.fMask = MIIM_STATE | MIIM_TYPE;\r
424           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
425             char buf[MSG_SIZ];\r
426             info.dwTypeData = buf;\r
427             info.cch = sizeof(buf);\r
428             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
429             if(i < 10) {
430                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
431                 else menuText[i][j] = strdup(buf); // remember original on first change\r
432             }\r
433             if(buf[0] == NULLCHAR) continue;\r
434             info.dwTypeData = T_(buf);\r
435             info.cch = strlen(buf)+1;\r
436             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
437           }\r
438     return subMenu;\r
439 }\r
440 \r
441 void\r
442 TranslateMenus(int addLanguage)\r
443 {\r
444     int i;\r
445     WIN32_FIND_DATA fileData;\r
446     HANDLE hFind;\r
447 #define IDM_English 1970\r
448     if(1) {\r
449         HMENU mainMenu = GetMenu(hwndMain);\r
450         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
451           HMENU subMenu = GetSubMenu(mainMenu, i);\r
452           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
453                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
454           TranslateOneMenu(i, subMenu);\r
455         }\r
456         DrawMenuBar(hwndMain);\r
457     }\r
458 \r
459     if(!addLanguage) return;\r
460     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
461         HMENU mainMenu = GetMenu(hwndMain);\r
462         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
463         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
464         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
465         i = 0; lastChecked = IDM_English;\r
466         do {\r
467             char *p, *q = fileData.cFileName;\r
468             int checkFlag = MF_UNCHECKED;\r
469             languageFile[i] = strdup(q);\r
470             if(barbaric && !strcmp(oldLanguage, q)) {\r
471                 checkFlag = MF_CHECKED;\r
472                 lastChecked = IDM_English + i + 1;\r
473                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
474             }\r
475             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
476             p = strstr(fileData.cFileName, ".lng");\r
477             if(p) *p = 0;\r
478             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
479         } while(FindNextFile(hFind, &fileData));\r
480         FindClose(hFind);\r
481     }\r
482 }\r
483 \r
484 #endif\r
485 \r
486 typedef struct {\r
487   char *name;\r
488   int squareSize;\r
489   int lineGap;\r
490   int smallLayout;\r
491   int tinyLayout;\r
492   int cliWidth, cliHeight;\r
493 } SizeInfo;\r
494 \r
495 SizeInfo sizeInfo[] = \r
496 {\r
497   { "tiny",     21, 0, 1, 1, 0, 0 },\r
498   { "teeny",    25, 1, 1, 1, 0, 0 },\r
499   { "dinky",    29, 1, 1, 1, 0, 0 },\r
500   { "petite",   33, 1, 1, 1, 0, 0 },\r
501   { "slim",     37, 2, 1, 0, 0, 0 },\r
502   { "small",    40, 2, 1, 0, 0, 0 },\r
503   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
504   { "middling", 49, 2, 0, 0, 0, 0 },\r
505   { "average",  54, 2, 0, 0, 0, 0 },\r
506   { "moderate", 58, 3, 0, 0, 0, 0 },\r
507   { "medium",   64, 3, 0, 0, 0, 0 },\r
508   { "bulky",    72, 3, 0, 0, 0, 0 },\r
509   { "large",    80, 3, 0, 0, 0, 0 },\r
510   { "big",      87, 3, 0, 0, 0, 0 },\r
511   { "huge",     95, 3, 0, 0, 0, 0 },\r
512   { "giant",    108, 3, 0, 0, 0, 0 },\r
513   { "colossal", 116, 4, 0, 0, 0, 0 },\r
514   { "titanic",  129, 4, 0, 0, 0, 0 },\r
515   { NULL, 0, 0, 0, 0, 0, 0 }\r
516 };\r
517 \r
518 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
519 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
520 {\r
521   { 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
522   { 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
523   { 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
524   { 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
525   { 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
526   { 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
527   { 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
528   { 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
529   { 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
530   { 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
531   { 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
532   { 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
533   { 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
534   { 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
535   { 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
536   { 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
537   { 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
538   { 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
539 };\r
540 \r
541 MyFont *font[NUM_SIZES][NUM_FONTS];\r
542 \r
543 typedef struct {\r
544   char *label;\r
545   int id;\r
546   HWND hwnd;\r
547   WNDPROC wndproc;\r
548 } MyButtonDesc;\r
549 \r
550 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
551 #define N_BUTTONS 5\r
552 \r
553 MyButtonDesc buttonDesc[N_BUTTONS] =\r
554 {\r
555   {"<<", IDM_ToStart, NULL, NULL},\r
556   {"<", IDM_Backward, NULL, NULL},\r
557   {"P", IDM_Pause, NULL, NULL},\r
558   {">", IDM_Forward, NULL, NULL},\r
559   {">>", IDM_ToEnd, NULL, NULL},\r
560 };\r
561 \r
562 int tinyLayout = 0, smallLayout = 0;\r
563 #define MENU_BAR_ITEMS 9\r
564 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
565   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
566   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
567 };\r
568 \r
569 \r
570 MySound sounds[(int)NSoundClasses];\r
571 MyTextAttribs textAttribs[(int)NColorClasses];\r
572 \r
573 MyColorizeAttribs colorizeAttribs[] = {\r
574   { (COLORREF)0, 0, N_("Shout Text") },\r
575   { (COLORREF)0, 0, N_("SShout/CShout") },\r
576   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
577   { (COLORREF)0, 0, N_("Channel Text") },\r
578   { (COLORREF)0, 0, N_("Kibitz Text") },\r
579   { (COLORREF)0, 0, N_("Tell Text") },\r
580   { (COLORREF)0, 0, N_("Challenge Text") },\r
581   { (COLORREF)0, 0, N_("Request Text") },\r
582   { (COLORREF)0, 0, N_("Seek Text") },\r
583   { (COLORREF)0, 0, N_("Normal Text") },\r
584   { (COLORREF)0, 0, N_("None") }\r
585 };\r
586 \r
587 \r
588 \r
589 static char *commentTitle;\r
590 static char *commentText;\r
591 static int commentIndex;\r
592 static Boolean editComment = FALSE;\r
593 \r
594 \r
595 char errorTitle[MSG_SIZ];\r
596 char errorMessage[2*MSG_SIZ];\r
597 HWND errorDialog = NULL;\r
598 BOOLEAN moveErrorMessageUp = FALSE;\r
599 BOOLEAN consoleEcho = TRUE;\r
600 CHARFORMAT consoleCF;\r
601 COLORREF consoleBackgroundColor;\r
602 \r
603 char *programVersion;\r
604 \r
605 #define CPReal 1\r
606 #define CPComm 2\r
607 #define CPSock 3\r
608 #define CPRcmd 4\r
609 typedef int CPKind;\r
610 \r
611 typedef struct {\r
612   CPKind kind;\r
613   HANDLE hProcess;\r
614   DWORD pid;\r
615   HANDLE hTo;\r
616   HANDLE hFrom;\r
617   SOCKET sock;\r
618   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
619 } ChildProc;\r
620 \r
621 #define INPUT_SOURCE_BUF_SIZE 4096\r
622 \r
623 typedef struct _InputSource {\r
624   CPKind kind;\r
625   HANDLE hFile;\r
626   SOCKET sock;\r
627   int lineByLine;\r
628   HANDLE hThread;\r
629   DWORD id;\r
630   char buf[INPUT_SOURCE_BUF_SIZE];\r
631   char *next;\r
632   DWORD count;\r
633   int error;\r
634   InputCallback func;\r
635   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
636   VOIDSTAR closure;\r
637 } InputSource;\r
638 \r
639 InputSource *consoleInputSource;\r
640 \r
641 DCB dcb;\r
642 \r
643 /* forward */\r
644 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
645 VOID ConsoleCreate();\r
646 LRESULT CALLBACK\r
647   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
648 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
649 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
650 VOID ParseCommSettings(char *arg, DCB *dcb);\r
651 LRESULT CALLBACK\r
652   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
653 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
654 void ParseIcsTextMenu(char *icsTextMenuString);\r
655 VOID PopUpMoveDialog(char firstchar);\r
656 VOID PopUpNameDialog(char firstchar);\r
657 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
658 \r
659 /* [AS] */\r
660 int NewGameFRC();\r
661 int GameListOptions();\r
662 \r
663 int dummy; // [HGM] for obsolete args\r
664 \r
665 HWND hwndMain = NULL;        /* root window*/\r
666 HWND hwndConsole = NULL;\r
667 HWND commentDialog = NULL;\r
668 HWND moveHistoryDialog = NULL;\r
669 HWND evalGraphDialog = NULL;\r
670 HWND engineOutputDialog = NULL;\r
671 HWND gameListDialog = NULL;\r
672 HWND editTagsDialog = NULL;\r
673 \r
674 int commentUp = FALSE;\r
675 \r
676 WindowPlacement wpMain;\r
677 WindowPlacement wpConsole;\r
678 WindowPlacement wpComment;\r
679 WindowPlacement wpMoveHistory;\r
680 WindowPlacement wpEvalGraph;\r
681 WindowPlacement wpEngineOutput;\r
682 WindowPlacement wpGameList;\r
683 WindowPlacement wpTags;\r
684 \r
685 VOID EngineOptionsPopup(); // [HGM] settings\r
686 \r
687 VOID GothicPopUp(char *title, VariantClass variant);\r
688 /*\r
689  * Setting "frozen" should disable all user input other than deleting\r
690  * the window.  We do this while engines are initializing themselves.\r
691  */\r
692 static int frozen = 0;\r
693 static int oldMenuItemState[MENU_BAR_ITEMS];\r
694 void FreezeUI()\r
695 {\r
696   HMENU hmenu;\r
697   int i;\r
698 \r
699   if (frozen) return;\r
700   frozen = 1;\r
701   hmenu = GetMenu(hwndMain);\r
702   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
703     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
704   }\r
705   DrawMenuBar(hwndMain);\r
706 }\r
707 \r
708 /* Undo a FreezeUI */\r
709 void ThawUI()\r
710 {\r
711   HMENU hmenu;\r
712   int i;\r
713 \r
714   if (!frozen) return;\r
715   frozen = 0;\r
716   hmenu = GetMenu(hwndMain);\r
717   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
718     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
719   }\r
720   DrawMenuBar(hwndMain);\r
721 }\r
722 \r
723 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
724 \r
725 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
726 #ifdef JAWS\r
727 #include "jaws.c"\r
728 #else\r
729 #define JAWS_INIT\r
730 #define JAWS_ARGS\r
731 #define JAWS_ALT_INTERCEPT\r
732 #define JAWS_KB_NAVIGATION\r
733 #define JAWS_MENU_ITEMS\r
734 #define JAWS_SILENCE\r
735 #define JAWS_REPLAY\r
736 #define JAWS_ACCEL\r
737 #define JAWS_COPYRIGHT\r
738 #define JAWS_DELETE(X) X\r
739 #define SAYMACHINEMOVE()\r
740 #define SAY(X)\r
741 #endif\r
742 \r
743 /*---------------------------------------------------------------------------*\\r
744  *\r
745  * WinMain\r
746  *\r
747 \*---------------------------------------------------------------------------*/\r
748 \r
749 int APIENTRY\r
750 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
751         LPSTR lpCmdLine, int nCmdShow)\r
752 {\r
753   MSG msg;\r
754   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
755 //  INITCOMMONCONTROLSEX ex;\r
756 \r
757   debugFP = stderr;\r
758 \r
759   LoadLibrary("RICHED32.DLL");\r
760   consoleCF.cbSize = sizeof(CHARFORMAT);\r
761 \r
762   if (!InitApplication(hInstance)) {\r
763     return (FALSE);\r
764   }\r
765   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
766     return (FALSE);\r
767   }\r
768 \r
769   JAWS_INIT\r
770   TranslateMenus(1);\r
771 \r
772 //  InitCommonControlsEx(&ex);\r
773   InitCommonControls();\r
774 \r
775   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
776   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
777   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
778 \r
779   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
780 \r
781   while (GetMessage(&msg, /* message structure */\r
782                     NULL, /* handle of window receiving the message */\r
783                     0,    /* lowest message to examine */\r
784                     0))   /* highest message to examine */\r
785     {\r
786 \r
787       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
788         // [HGM] navigate: switch between all windows with tab\r
789         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
790         int i, currentElement = 0;\r
791 \r
792         // first determine what element of the chain we come from (if any)\r
793         if(appData.icsActive) {\r
794             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
795             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
796         }\r
797         if(engineOutputDialog && EngineOutputIsUp()) {\r
798             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
799             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
800         }\r
801         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
802             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
803         }\r
804         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
805         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
806         if(msg.hwnd == e1)                 currentElement = 2; else\r
807         if(msg.hwnd == e2)                 currentElement = 3; else\r
808         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
809         if(msg.hwnd == mh)                currentElement = 4; else\r
810         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
811         if(msg.hwnd == hText)  currentElement = 5; else\r
812         if(msg.hwnd == hInput) currentElement = 6; else\r
813         for (i = 0; i < N_BUTTONS; i++) {\r
814             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
815         }\r
816 \r
817         // determine where to go to\r
818         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
819           do {\r
820             currentElement = (currentElement + direction) % 7;\r
821             switch(currentElement) {\r
822                 case 0:\r
823                   h = hwndMain; break; // passing this case always makes the loop exit\r
824                 case 1:\r
825                   h = buttonDesc[0].hwnd; break; // could be NULL\r
826                 case 2:\r
827                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
828                   h = e1; break;\r
829                 case 3:\r
830                   if(!EngineOutputIsUp()) continue;\r
831                   h = e2; break;\r
832                 case 4:\r
833                   if(!MoveHistoryIsUp()) continue;\r
834                   h = mh; break;\r
835 //              case 6: // input to eval graph does not seem to get here!\r
836 //                if(!EvalGraphIsUp()) continue;\r
837 //                h = evalGraphDialog; break;\r
838                 case 5:\r
839                   if(!appData.icsActive) continue;\r
840                   SAY("display");\r
841                   h = hText; break;\r
842                 case 6:\r
843                   if(!appData.icsActive) continue;\r
844                   SAY("input");\r
845                   h = hInput; break;\r
846             }\r
847           } while(h == 0);\r
848 \r
849           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
850           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
851           SetFocus(h);\r
852 \r
853           continue; // this message now has been processed\r
854         }\r
855       }\r
856 \r
857       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
858           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
859           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
860           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
861           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
862           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
863           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
864           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
865           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
866           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
867         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
868         for(i=0; i<MAX_CHAT; i++) \r
869             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
870                 done = 1; break;\r
871         }\r
872         if(done) continue; // [HGM] chat: end patch\r
873         TranslateMessage(&msg); /* Translates virtual key codes */\r
874         DispatchMessage(&msg);  /* Dispatches message to window */\r
875       }\r
876     }\r
877 \r
878 \r
879   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
880 }\r
881 \r
882 /*---------------------------------------------------------------------------*\\r
883  *\r
884  * Initialization functions\r
885  *\r
886 \*---------------------------------------------------------------------------*/\r
887 \r
888 void\r
889 SetUserLogo()\r
890 {   // update user logo if necessary\r
891     static char oldUserName[MSG_SIZ], *curName;\r
892 \r
893     if(appData.autoLogo) {\r
894           curName = UserName();\r
895           if(strcmp(curName, oldUserName)) {\r
896             snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
897                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
898                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
899                 if(userLogo == NULL)\r
900                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
901           }\r
902     }\r
903 }\r
904 \r
905 BOOL\r
906 InitApplication(HINSTANCE hInstance)\r
907 {\r
908   WNDCLASS wc;\r
909 \r
910   /* Fill in window class structure with parameters that describe the */\r
911   /* main window. */\r
912 \r
913   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
914   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
915   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
916   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
917   wc.hInstance     = hInstance;         /* Owner of this class */\r
918   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
919   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
920   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
921   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
922   wc.lpszClassName = szAppName;                 /* Name to register as */\r
923 \r
924   /* Register the window class and return success/failure code. */\r
925   if (!RegisterClass(&wc)) return FALSE;\r
926 \r
927   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
928   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
929   wc.cbClsExtra    = 0;\r
930   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
931   wc.hInstance     = hInstance;\r
932   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
933   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
934   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
935   wc.lpszMenuName  = NULL;\r
936   wc.lpszClassName = szConsoleName;\r
937 \r
938   if (!RegisterClass(&wc)) return FALSE;\r
939   return TRUE;\r
940 }\r
941 \r
942 \r
943 /* Set by InitInstance, used by EnsureOnScreen */\r
944 int screenHeight, screenWidth;\r
945 \r
946 void\r
947 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
948 {\r
949 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
950   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
951   if (*x > screenWidth - 32) *x = 0;\r
952   if (*y > screenHeight - 32) *y = 0;\r
953   if (*x < minX) *x = minX;\r
954   if (*y < minY) *y = minY;\r
955 }\r
956 \r
957 BOOL\r
958 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
959 {\r
960   HWND hwnd; /* Main window handle. */\r
961   int ibs;\r
962   WINDOWPLACEMENT wp;\r
963   char *filepart;\r
964 \r
965   hInst = hInstance;    /* Store instance handle in our global variable */\r
966   programName = szAppName;\r
967 \r
968   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
969     *filepart = NULLCHAR;\r
970   } else {\r
971     GetCurrentDirectory(MSG_SIZ, installDir);\r
972   }\r
973   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
974   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
975   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
976   /* xboard, and older WinBoards, controlled the move sound with the\r
977      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
978      always turn the option on (so that the backend will call us),\r
979      then let the user turn the sound off by setting it to silence if\r
980      desired.  To accommodate old winboard.ini files saved by old\r
981      versions of WinBoard, we also turn off the sound if the option\r
982      was initially set to false. [HGM] taken out of InitAppData */\r
983   if (!appData.ringBellAfterMoves) {\r
984     sounds[(int)SoundMove].name = strdup("");\r
985     appData.ringBellAfterMoves = TRUE;\r
986   }\r
987   if (appData.debugMode) {\r
988     debugFP = fopen(appData.nameOfDebugFile, "w");\r
989     setbuf(debugFP, NULL);\r
990   }\r
991 \r
992   LoadLanguageFile(appData.language);\r
993 \r
994   InitBackEnd1();\r
995 \r
996 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
997 //  InitEngineUCI( installDir, &second );\r
998 \r
999   /* Create a main window for this application instance. */\r
1000   hwnd = CreateWindow(szAppName, szTitle,\r
1001                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1002                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1003                       NULL, NULL, hInstance, NULL);\r
1004   hwndMain = hwnd;\r
1005 \r
1006   /* If window could not be created, return "failure" */\r
1007   if (!hwnd) {\r
1008     return (FALSE);\r
1009   }\r
1010 \r
1011   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1012   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
1013       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1014 \r
1015       if (first.programLogo == NULL && appData.debugMode) {\r
1016           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
1017       }\r
1018   } else if(appData.autoLogo) {\r
1019       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
1020         char buf[MSG_SIZ];\r
1021           snprintf(buf, MSG_SIZ, "%s/logo.bmp", appData.firstDirectory);\r
1022         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
1023       }\r
1024   }\r
1025 \r
1026   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
1027       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1028 \r
1029       if (second.programLogo == NULL && appData.debugMode) {\r
1030           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
1031       }\r
1032   } else if(appData.autoLogo) {\r
1033       char buf[MSG_SIZ];\r
1034       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
1035         snprintf(buf, MSG_SIZ, "logos\\%s.bmp", appData.icsHost);\r
1036         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1037       } else\r
1038       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
1039         snprintf(buf, MSG_SIZ, "%s\\logo.bmp", appData.secondDirectory);\r
1040         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
1041       }\r
1042   }\r
1043 \r
1044   SetUserLogo();\r
1045 \r
1046   iconWhite = LoadIcon(hInstance, "icon_white");\r
1047   iconBlack = LoadIcon(hInstance, "icon_black");\r
1048   iconCurrent = iconWhite;\r
1049   InitDrawingColors();\r
1050   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1051   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1052   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1053     /* Compute window size for each board size, and use the largest\r
1054        size that fits on this screen as the default. */\r
1055     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1056     if (boardSize == (BoardSize)-1 &&\r
1057         winH <= screenHeight\r
1058            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1059         && winW <= screenWidth) {\r
1060       boardSize = (BoardSize)ibs;\r
1061     }\r
1062   }\r
1063 \r
1064   InitDrawingSizes(boardSize, 0);\r
1065   InitMenuChecks();\r
1066   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1067 \r
1068   /* [AS] Load textures if specified */\r
1069   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1070   \r
1071   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1072       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1073       liteBackTextureMode = appData.liteBackTextureMode;\r
1074 \r
1075       if (liteBackTexture == NULL && appData.debugMode) {\r
1076           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1077       }\r
1078   }\r
1079   \r
1080   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1081       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1082       darkBackTextureMode = appData.darkBackTextureMode;\r
1083 \r
1084       if (darkBackTexture == NULL && appData.debugMode) {\r
1085           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1086       }\r
1087   }\r
1088 \r
1089   mysrandom( (unsigned) time(NULL) );\r
1090 \r
1091   /* [AS] Restore layout */\r
1092   if( wpMoveHistory.visible ) {\r
1093       MoveHistoryPopUp();\r
1094   }\r
1095 \r
1096   if( wpEvalGraph.visible ) {\r
1097       EvalGraphPopUp();\r
1098   }\r
1099 \r
1100   if( wpEngineOutput.visible ) {\r
1101       EngineOutputPopUp();\r
1102   }\r
1103 \r
1104   /* Make the window visible; update its client area; and return "success" */\r
1105   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1106   wp.length = sizeof(WINDOWPLACEMENT);\r
1107   wp.flags = 0;\r
1108   wp.showCmd = nCmdShow;\r
1109   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1110   wp.rcNormalPosition.left = wpMain.x;\r
1111   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1112   wp.rcNormalPosition.top = wpMain.y;\r
1113   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1114   SetWindowPlacement(hwndMain, &wp);\r
1115 \r
1116   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1117 \r
1118   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1119                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1120 \r
1121   if (hwndConsole) {\r
1122 #if AOT_CONSOLE\r
1123     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1124                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1125 #endif\r
1126     ShowWindow(hwndConsole, nCmdShow);\r
1127     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
1128       char buf[MSG_SIZ], *p = buf, *q;\r
1129         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
1130       do {\r
1131         q = strchr(p, ';');\r
1132         if(q) *q++ = 0;\r
1133         if(*p) ChatPopUp(p);\r
1134       } while(p=q);\r
1135     }\r
1136     SetActiveWindow(hwndConsole);\r
1137   }\r
1138   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1139   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1140 \r
1141   return TRUE;\r
1142 \r
1143 }\r
1144 \r
1145 VOID\r
1146 InitMenuChecks()\r
1147 {\r
1148   HMENU hmenu = GetMenu(hwndMain);\r
1149 \r
1150   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1151                         MF_BYCOMMAND|((appData.icsActive &&\r
1152                                        *appData.icsCommPort != NULLCHAR) ?\r
1153                                       MF_ENABLED : MF_GRAYED));\r
1154   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1155                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1156                                      MF_CHECKED : MF_UNCHECKED));\r
1157 }\r
1158 \r
1159 //---------------------------------------------------------------------------------------------------------\r
1160 \r
1161 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1162 #define XBOARD FALSE\r
1163 \r
1164 #define OPTCHAR "/"\r
1165 #define SEPCHAR "="\r
1166 \r
1167 #include "args.h"\r
1168 \r
1169 // front-end part of option handling\r
1170 \r
1171 VOID\r
1172 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1173 {\r
1174   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1175   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1176   DeleteDC(hdc);\r
1177   lf->lfWidth = 0;\r
1178   lf->lfEscapement = 0;\r
1179   lf->lfOrientation = 0;\r
1180   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1181   lf->lfItalic = mfp->italic;\r
1182   lf->lfUnderline = mfp->underline;\r
1183   lf->lfStrikeOut = mfp->strikeout;\r
1184   lf->lfCharSet = mfp->charset;\r
1185   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1186   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1187   lf->lfQuality = DEFAULT_QUALITY;\r
1188   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1189     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1190 }\r
1191 \r
1192 void\r
1193 CreateFontInMF(MyFont *mf)\r
1194\r
1195   LFfromMFP(&mf->lf, &mf->mfp);\r
1196   if (mf->hf) DeleteObject(mf->hf);\r
1197   mf->hf = CreateFontIndirect(&mf->lf);\r
1198 }\r
1199 \r
1200 // [HGM] This platform-dependent table provides the location for storing the color info\r
1201 void *\r
1202 colorVariable[] = {\r
1203   &whitePieceColor, \r
1204   &blackPieceColor, \r
1205   &lightSquareColor,\r
1206   &darkSquareColor, \r
1207   &highlightSquareColor,\r
1208   &premoveHighlightColor,\r
1209   NULL,\r
1210   &consoleBackgroundColor,\r
1211   &appData.fontForeColorWhite,\r
1212   &appData.fontBackColorWhite,\r
1213   &appData.fontForeColorBlack,\r
1214   &appData.fontBackColorBlack,\r
1215   &appData.evalHistColorWhite,\r
1216   &appData.evalHistColorBlack,\r
1217   &appData.highlightArrowColor,\r
1218 };\r
1219 \r
1220 /* Command line font name parser.  NULL name means do nothing.\r
1221    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1222    For backward compatibility, syntax without the colon is also\r
1223    accepted, but font names with digits in them won't work in that case.\r
1224 */\r
1225 VOID\r
1226 ParseFontName(char *name, MyFontParams *mfp)\r
1227 {\r
1228   char *p, *q;\r
1229   if (name == NULL) return;\r
1230   p = name;\r
1231   q = strchr(p, ':');\r
1232   if (q) {\r
1233     if (q - p >= sizeof(mfp->faceName))\r
1234       ExitArgError(_("Font name too long:"), name);\r
1235     memcpy(mfp->faceName, p, q - p);\r
1236     mfp->faceName[q - p] = NULLCHAR;\r
1237     p = q + 1;\r
1238   } else {\r
1239     q = mfp->faceName;\r
1240     while (*p && !isdigit(*p)) {\r
1241       *q++ = *p++;\r
1242       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1243         ExitArgError(_("Font name too long:"), name);\r
1244     }\r
1245     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1246     *q = NULLCHAR;\r
1247   }\r
1248   if (!*p) ExitArgError(_("Font point size missing:"), name);\r
1249   mfp->pointSize = (float) atof(p);\r
1250   mfp->bold = (strchr(p, 'b') != NULL);\r
1251   mfp->italic = (strchr(p, 'i') != NULL);\r
1252   mfp->underline = (strchr(p, 'u') != NULL);\r
1253   mfp->strikeout = (strchr(p, 's') != NULL);\r
1254   mfp->charset = DEFAULT_CHARSET;\r
1255   q = strchr(p, 'c');\r
1256   if (q)\r
1257     mfp->charset = (BYTE) atoi(q+1);\r
1258 }\r
1259 \r
1260 void\r
1261 ParseFont(char *name, int number)\r
1262 { // wrapper to shield back-end from 'font'\r
1263   ParseFontName(name, &font[boardSize][number]->mfp);\r
1264 }\r
1265 \r
1266 void\r
1267 SetFontDefaults()\r
1268 { // in WB  we have a 2D array of fonts; this initializes their description\r
1269   int i, j;\r
1270   /* Point font array elements to structures and\r
1271      parse default font names */\r
1272   for (i=0; i<NUM_FONTS; i++) {\r
1273     for (j=0; j<NUM_SIZES; j++) {\r
1274       font[j][i] = &fontRec[j][i];\r
1275       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1276     }\r
1277   }\r
1278 }\r
1279 \r
1280 void\r
1281 CreateFonts()\r
1282 { // here we create the actual fonts from the selected descriptions\r
1283   int i, j;\r
1284   for (i=0; i<NUM_FONTS; i++) {\r
1285     for (j=0; j<NUM_SIZES; j++) {\r
1286       CreateFontInMF(font[j][i]);\r
1287     }\r
1288   }\r
1289 }\r
1290 /* Color name parser.\r
1291    X version accepts X color names, but this one\r
1292    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1293 COLORREF\r
1294 ParseColorName(char *name)\r
1295 {\r
1296   int red, green, blue, count;\r
1297   char buf[MSG_SIZ];\r
1298 \r
1299   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1300   if (count != 3) {\r
1301     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1302       &red, &green, &blue);\r
1303   }\r
1304   if (count != 3) {\r
1305     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1306     DisplayError(buf, 0);\r
1307     return RGB(0, 0, 0);\r
1308   }\r
1309   return PALETTERGB(red, green, blue);\r
1310 }\r
1311 \r
1312 void\r
1313 ParseColor(int n, char *name)\r
1314 { // for WinBoard the color is an int, which needs to be derived from the string\r
1315   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1316 }\r
1317 \r
1318 void\r
1319 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1320 {\r
1321   char *e = argValue;\r
1322   int eff = 0;\r
1323 \r
1324   while (*e) {\r
1325     if (*e == 'b')      eff |= CFE_BOLD;\r
1326     else if (*e == 'i') eff |= CFE_ITALIC;\r
1327     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1328     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1329     else if (*e == '#' || isdigit(*e)) break;\r
1330     e++;\r
1331   }\r
1332   *effects = eff;\r
1333   *color   = ParseColorName(e);\r
1334 }\r
1335 \r
1336 void\r
1337 ParseTextAttribs(ColorClass cc, char *s)\r
1338 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1339     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1340     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1341 }\r
1342 \r
1343 void\r
1344 ParseBoardSize(void *addr, char *name)\r
1345 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1346   BoardSize bs = SizeTiny;\r
1347   while (sizeInfo[bs].name != NULL) {\r
1348     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1349         *(BoardSize *)addr = bs;\r
1350         return;\r
1351     }\r
1352     bs++;\r
1353   }\r
1354   ExitArgError(_("Unrecognized board size value"), name);\r
1355 }\r
1356 \r
1357 void\r
1358 LoadAllSounds()\r
1359 { // [HGM] import name from appData first\r
1360   ColorClass cc;\r
1361   SoundClass sc;\r
1362   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1363     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1364     textAttribs[cc].sound.data = NULL;\r
1365     MyLoadSound(&textAttribs[cc].sound);\r
1366   }\r
1367   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1368     textAttribs[cc].sound.name = strdup("");\r
1369     textAttribs[cc].sound.data = NULL;\r
1370   }\r
1371   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1372     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1373     sounds[sc].data = NULL;\r
1374     MyLoadSound(&sounds[sc]);\r
1375   }\r
1376 }\r
1377 \r
1378 void\r
1379 SetCommPortDefaults()\r
1380 {\r
1381    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1382   dcb.DCBlength = sizeof(DCB);\r
1383   dcb.BaudRate = 9600;\r
1384   dcb.fBinary = TRUE;\r
1385   dcb.fParity = FALSE;\r
1386   dcb.fOutxCtsFlow = FALSE;\r
1387   dcb.fOutxDsrFlow = FALSE;\r
1388   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1389   dcb.fDsrSensitivity = FALSE;\r
1390   dcb.fTXContinueOnXoff = TRUE;\r
1391   dcb.fOutX = FALSE;\r
1392   dcb.fInX = FALSE;\r
1393   dcb.fNull = FALSE;\r
1394   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1395   dcb.fAbortOnError = FALSE;\r
1396   dcb.ByteSize = 7;\r
1397   dcb.Parity = SPACEPARITY;\r
1398   dcb.StopBits = ONESTOPBIT;\r
1399 }\r
1400 \r
1401 // [HGM] args: these three cases taken out to stay in front-end\r
1402 void\r
1403 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1404 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1405         // while the curent board size determines the element. This system should be ported to XBoard.\r
1406         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1407         int bs;\r
1408         for (bs=0; bs<NUM_SIZES; bs++) {\r
1409           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1410           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1411           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1412             ad->argName, mfp->faceName, mfp->pointSize,\r
1413             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1414             mfp->bold ? "b" : "",\r
1415             mfp->italic ? "i" : "",\r
1416             mfp->underline ? "u" : "",\r
1417             mfp->strikeout ? "s" : "",\r
1418             (int)mfp->charset);\r
1419         }\r
1420       }\r
1421 \r
1422 void\r
1423 ExportSounds()\r
1424 { // [HGM] copy the names from the internal WB variables to appData\r
1425   ColorClass cc;\r
1426   SoundClass sc;\r
1427   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1428     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1429   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1430     (&appData.soundMove)[sc] = sounds[sc].name;\r
1431 }\r
1432 \r
1433 void\r
1434 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1435 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1436         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1437         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1438           (ta->effects & CFE_BOLD) ? "b" : "",\r
1439           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1440           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1441           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1442           (ta->effects) ? " " : "",\r
1443           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1444       }\r
1445 \r
1446 void\r
1447 SaveColor(FILE *f, ArgDescriptor *ad)\r
1448 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1449         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1450         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1451           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1452 }\r
1453 \r
1454 void\r
1455 SaveBoardSize(FILE *f, char *name, void *addr)\r
1456 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1457   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1458 }\r
1459 \r
1460 void\r
1461 ParseCommPortSettings(char *s)\r
1462 { // wrapper to keep dcb from back-end\r
1463   ParseCommSettings(s, &dcb);\r
1464 }\r
1465 \r
1466 void\r
1467 GetWindowCoords()\r
1468 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1469   GetActualPlacement(hwndMain, &wpMain);\r
1470   GetActualPlacement(hwndConsole, &wpConsole);\r
1471   GetActualPlacement(commentDialog, &wpComment);\r
1472   GetActualPlacement(editTagsDialog, &wpTags);\r
1473   GetActualPlacement(gameListDialog, &wpGameList);\r
1474   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1475   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1476   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1477 }\r
1478 \r
1479 void\r
1480 PrintCommPortSettings(FILE *f, char *name)\r
1481 { // wrapper to shield back-end from DCB\r
1482       PrintCommSettings(f, name, &dcb);\r
1483 }\r
1484 \r
1485 int\r
1486 MySearchPath(char *installDir, char *name, char *fullname)\r
1487 {\r
1488   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1489   if(name[0]== '%') {\r
1490     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1491     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1492       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1493       *strchr(buf, '%') = 0;\r
1494       strcat(fullname, getenv(buf));\r
1495       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1496     }\r
1497     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1498     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1499     return (int) strlen(fullname);\r
1500   }\r
1501   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1502 }\r
1503 \r
1504 int\r
1505 MyGetFullPathName(char *name, char *fullname)\r
1506 {\r
1507   char *dummy;\r
1508   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1509 }\r
1510 \r
1511 int\r
1512 MainWindowUp()\r
1513 { // [HGM] args: allows testing if main window is realized from back-end\r
1514   return hwndMain != NULL;\r
1515 }\r
1516 \r
1517 void\r
1518 PopUpStartupDialog()\r
1519 {\r
1520     FARPROC lpProc;\r
1521     \r
1522     LoadLanguageFile(appData.language);\r
1523     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1524     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1525     FreeProcInstance(lpProc);\r
1526 }\r
1527 \r
1528 /*---------------------------------------------------------------------------*\\r
1529  *\r
1530  * GDI board drawing routines\r
1531  *\r
1532 \*---------------------------------------------------------------------------*/\r
1533 \r
1534 /* [AS] Draw square using background texture */\r
1535 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1536 {\r
1537     XFORM   x;\r
1538 \r
1539     if( mode == 0 ) {\r
1540         return; /* Should never happen! */\r
1541     }\r
1542 \r
1543     SetGraphicsMode( dst, GM_ADVANCED );\r
1544 \r
1545     switch( mode ) {\r
1546     case 1:\r
1547         /* Identity */\r
1548         break;\r
1549     case 2:\r
1550         /* X reflection */\r
1551         x.eM11 = -1.0;\r
1552         x.eM12 = 0;\r
1553         x.eM21 = 0;\r
1554         x.eM22 = 1.0;\r
1555         x.eDx = (FLOAT) dw + dx - 1;\r
1556         x.eDy = 0;\r
1557         dx = 0;\r
1558         SetWorldTransform( dst, &x );\r
1559         break;\r
1560     case 3:\r
1561         /* Y reflection */\r
1562         x.eM11 = 1.0;\r
1563         x.eM12 = 0;\r
1564         x.eM21 = 0;\r
1565         x.eM22 = -1.0;\r
1566         x.eDx = 0;\r
1567         x.eDy = (FLOAT) dh + dy - 1;\r
1568         dy = 0;\r
1569         SetWorldTransform( dst, &x );\r
1570         break;\r
1571     case 4:\r
1572         /* X/Y flip */\r
1573         x.eM11 = 0;\r
1574         x.eM12 = 1.0;\r
1575         x.eM21 = 1.0;\r
1576         x.eM22 = 0;\r
1577         x.eDx = (FLOAT) dx;\r
1578         x.eDy = (FLOAT) dy;\r
1579         dx = 0;\r
1580         dy = 0;\r
1581         SetWorldTransform( dst, &x );\r
1582         break;\r
1583     }\r
1584 \r
1585     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1586 \r
1587     x.eM11 = 1.0;\r
1588     x.eM12 = 0;\r
1589     x.eM21 = 0;\r
1590     x.eM22 = 1.0;\r
1591     x.eDx = 0;\r
1592     x.eDy = 0;\r
1593     SetWorldTransform( dst, &x );\r
1594 \r
1595     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1596 }\r
1597 \r
1598 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1599 enum {\r
1600     PM_WP = (int) WhitePawn, \r
1601     PM_WN = (int) WhiteKnight, \r
1602     PM_WB = (int) WhiteBishop, \r
1603     PM_WR = (int) WhiteRook, \r
1604     PM_WQ = (int) WhiteQueen, \r
1605     PM_WF = (int) WhiteFerz, \r
1606     PM_WW = (int) WhiteWazir, \r
1607     PM_WE = (int) WhiteAlfil, \r
1608     PM_WM = (int) WhiteMan, \r
1609     PM_WO = (int) WhiteCannon, \r
1610     PM_WU = (int) WhiteUnicorn, \r
1611     PM_WH = (int) WhiteNightrider, \r
1612     PM_WA = (int) WhiteAngel, \r
1613     PM_WC = (int) WhiteMarshall, \r
1614     PM_WAB = (int) WhiteCardinal, \r
1615     PM_WD = (int) WhiteDragon, \r
1616     PM_WL = (int) WhiteLance, \r
1617     PM_WS = (int) WhiteCobra, \r
1618     PM_WV = (int) WhiteFalcon, \r
1619     PM_WSG = (int) WhiteSilver, \r
1620     PM_WG = (int) WhiteGrasshopper, \r
1621     PM_WK = (int) WhiteKing,\r
1622     PM_BP = (int) BlackPawn, \r
1623     PM_BN = (int) BlackKnight, \r
1624     PM_BB = (int) BlackBishop, \r
1625     PM_BR = (int) BlackRook, \r
1626     PM_BQ = (int) BlackQueen, \r
1627     PM_BF = (int) BlackFerz, \r
1628     PM_BW = (int) BlackWazir, \r
1629     PM_BE = (int) BlackAlfil, \r
1630     PM_BM = (int) BlackMan,\r
1631     PM_BO = (int) BlackCannon, \r
1632     PM_BU = (int) BlackUnicorn, \r
1633     PM_BH = (int) BlackNightrider, \r
1634     PM_BA = (int) BlackAngel, \r
1635     PM_BC = (int) BlackMarshall, \r
1636     PM_BG = (int) BlackGrasshopper, \r
1637     PM_BAB = (int) BlackCardinal,\r
1638     PM_BD = (int) BlackDragon,\r
1639     PM_BL = (int) BlackLance,\r
1640     PM_BS = (int) BlackCobra,\r
1641     PM_BV = (int) BlackFalcon,\r
1642     PM_BSG = (int) BlackSilver,\r
1643     PM_BK = (int) BlackKing\r
1644 };\r
1645 \r
1646 static HFONT hPieceFont = NULL;\r
1647 static HBITMAP hPieceMask[(int) EmptySquare];\r
1648 static HBITMAP hPieceFace[(int) EmptySquare];\r
1649 static int fontBitmapSquareSize = 0;\r
1650 static char pieceToFontChar[(int) EmptySquare] =\r
1651                               { 'p', 'n', 'b', 'r', 'q', \r
1652                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1653                       'k', 'o', 'm', 'v', 't', 'w', \r
1654                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1655                                                               'l' };\r
1656 \r
1657 extern BOOL SetCharTable( char *table, const char * map );\r
1658 /* [HGM] moved to backend.c */\r
1659 \r
1660 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1661 {\r
1662     HBRUSH hbrush;\r
1663     BYTE r1 = GetRValue( color );\r
1664     BYTE g1 = GetGValue( color );\r
1665     BYTE b1 = GetBValue( color );\r
1666     BYTE r2 = r1 / 2;\r
1667     BYTE g2 = g1 / 2;\r
1668     BYTE b2 = b1 / 2;\r
1669     RECT rc;\r
1670 \r
1671     /* Create a uniform background first */\r
1672     hbrush = CreateSolidBrush( color );\r
1673     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1674     FillRect( hdc, &rc, hbrush );\r
1675     DeleteObject( hbrush );\r
1676     \r
1677     if( mode == 1 ) {\r
1678         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1679         int steps = squareSize / 2;\r
1680         int i;\r
1681 \r
1682         for( i=0; i<steps; i++ ) {\r
1683             BYTE r = r1 - (r1-r2) * i / steps;\r
1684             BYTE g = g1 - (g1-g2) * i / steps;\r
1685             BYTE b = b1 - (b1-b2) * i / steps;\r
1686 \r
1687             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1688             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1689             FillRect( hdc, &rc, hbrush );\r
1690             DeleteObject(hbrush);\r
1691         }\r
1692     }\r
1693     else if( mode == 2 ) {\r
1694         /* Diagonal gradient, good more or less for every piece */\r
1695         POINT triangle[3];\r
1696         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1697         HBRUSH hbrush_old;\r
1698         int steps = squareSize;\r
1699         int i;\r
1700 \r
1701         triangle[0].x = squareSize - steps;\r
1702         triangle[0].y = squareSize;\r
1703         triangle[1].x = squareSize;\r
1704         triangle[1].y = squareSize;\r
1705         triangle[2].x = squareSize;\r
1706         triangle[2].y = squareSize - steps;\r
1707 \r
1708         for( i=0; i<steps; i++ ) {\r
1709             BYTE r = r1 - (r1-r2) * i / steps;\r
1710             BYTE g = g1 - (g1-g2) * i / steps;\r
1711             BYTE b = b1 - (b1-b2) * i / steps;\r
1712 \r
1713             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1714             hbrush_old = SelectObject( hdc, hbrush );\r
1715             Polygon( hdc, triangle, 3 );\r
1716             SelectObject( hdc, hbrush_old );\r
1717             DeleteObject(hbrush);\r
1718             triangle[0].x++;\r
1719             triangle[2].y++;\r
1720         }\r
1721 \r
1722         SelectObject( hdc, hpen );\r
1723     }\r
1724 }\r
1725 \r
1726 /*\r
1727     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1728     seems to work ok. The main problem here is to find the "inside" of a chess\r
1729     piece: follow the steps as explained below.\r
1730 */\r
1731 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1732 {\r
1733     HBITMAP hbm;\r
1734     HBITMAP hbm_old;\r
1735     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1736     RECT rc;\r
1737     SIZE sz;\r
1738     POINT pt;\r
1739     int backColor = whitePieceColor; \r
1740     int foreColor = blackPieceColor;\r
1741     \r
1742     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1743         backColor = appData.fontBackColorWhite;\r
1744         foreColor = appData.fontForeColorWhite;\r
1745     }\r
1746     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1747         backColor = appData.fontBackColorBlack;\r
1748         foreColor = appData.fontForeColorBlack;\r
1749     }\r
1750 \r
1751     /* Mask */\r
1752     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1753 \r
1754     hbm_old = SelectObject( hdc, hbm );\r
1755 \r
1756     rc.left = 0;\r
1757     rc.top = 0;\r
1758     rc.right = squareSize;\r
1759     rc.bottom = squareSize;\r
1760 \r
1761     /* Step 1: background is now black */\r
1762     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1763 \r
1764     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1765 \r
1766     pt.x = (squareSize - sz.cx) / 2;\r
1767     pt.y = (squareSize - sz.cy) / 2;\r
1768 \r
1769     SetBkMode( hdc, TRANSPARENT );\r
1770     SetTextColor( hdc, chroma );\r
1771     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1772     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1773 \r
1774     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1775     /* Step 3: the area outside the piece is filled with white */\r
1776 //    FloodFill( hdc, 0, 0, chroma );\r
1777     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1778     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1779     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1780     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1781     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1782     /* \r
1783         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1784         but if the start point is not inside the piece we're lost!\r
1785         There should be a better way to do this... if we could create a region or path\r
1786         from the fill operation we would be fine for example.\r
1787     */\r
1788 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1789     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1790 \r
1791     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1792         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1793         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1794 \r
1795         SelectObject( dc2, bm2 );\r
1796         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1797         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1798         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1799         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1800         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1801 \r
1802         DeleteDC( dc2 );\r
1803         DeleteObject( bm2 );\r
1804     }\r
1805 \r
1806     SetTextColor( hdc, 0 );\r
1807     /* \r
1808         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1809         draw the piece again in black for safety.\r
1810     */\r
1811     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1812 \r
1813     SelectObject( hdc, hbm_old );\r
1814 \r
1815     if( hPieceMask[index] != NULL ) {\r
1816         DeleteObject( hPieceMask[index] );\r
1817     }\r
1818 \r
1819     hPieceMask[index] = hbm;\r
1820 \r
1821     /* Face */\r
1822     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1823 \r
1824     SelectObject( hdc, hbm );\r
1825 \r
1826     {\r
1827         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1828         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1829         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1830 \r
1831         SelectObject( dc1, hPieceMask[index] );\r
1832         SelectObject( dc2, bm2 );\r
1833         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1834         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1835         \r
1836         /* \r
1837             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1838             the piece background and deletes (makes transparent) the rest.\r
1839             Thanks to that mask, we are free to paint the background with the greates\r
1840             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1841             We use this, to make gradients and give the pieces a "roundish" look.\r
1842         */\r
1843         SetPieceBackground( hdc, backColor, 2 );\r
1844         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1845 \r
1846         DeleteDC( dc2 );\r
1847         DeleteDC( dc1 );\r
1848         DeleteObject( bm2 );\r
1849     }\r
1850 \r
1851     SetTextColor( hdc, foreColor );\r
1852     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1853 \r
1854     SelectObject( hdc, hbm_old );\r
1855 \r
1856     if( hPieceFace[index] != NULL ) {\r
1857         DeleteObject( hPieceFace[index] );\r
1858     }\r
1859 \r
1860     hPieceFace[index] = hbm;\r
1861 }\r
1862 \r
1863 static int TranslatePieceToFontPiece( int piece )\r
1864 {\r
1865     switch( piece ) {\r
1866     case BlackPawn:\r
1867         return PM_BP;\r
1868     case BlackKnight:\r
1869         return PM_BN;\r
1870     case BlackBishop:\r
1871         return PM_BB;\r
1872     case BlackRook:\r
1873         return PM_BR;\r
1874     case BlackQueen:\r
1875         return PM_BQ;\r
1876     case BlackKing:\r
1877         return PM_BK;\r
1878     case WhitePawn:\r
1879         return PM_WP;\r
1880     case WhiteKnight:\r
1881         return PM_WN;\r
1882     case WhiteBishop:\r
1883         return PM_WB;\r
1884     case WhiteRook:\r
1885         return PM_WR;\r
1886     case WhiteQueen:\r
1887         return PM_WQ;\r
1888     case WhiteKing:\r
1889         return PM_WK;\r
1890 \r
1891     case BlackAngel:\r
1892         return PM_BA;\r
1893     case BlackMarshall:\r
1894         return PM_BC;\r
1895     case BlackFerz:\r
1896         return PM_BF;\r
1897     case BlackNightrider:\r
1898         return PM_BH;\r
1899     case BlackAlfil:\r
1900         return PM_BE;\r
1901     case BlackWazir:\r
1902         return PM_BW;\r
1903     case BlackUnicorn:\r
1904         return PM_BU;\r
1905     case BlackCannon:\r
1906         return PM_BO;\r
1907     case BlackGrasshopper:\r
1908         return PM_BG;\r
1909     case BlackMan:\r
1910         return PM_BM;\r
1911     case BlackSilver:\r
1912         return PM_BSG;\r
1913     case BlackLance:\r
1914         return PM_BL;\r
1915     case BlackFalcon:\r
1916         return PM_BV;\r
1917     case BlackCobra:\r
1918         return PM_BS;\r
1919     case BlackCardinal:\r
1920         return PM_BAB;\r
1921     case BlackDragon:\r
1922         return PM_BD;\r
1923 \r
1924     case WhiteAngel:\r
1925         return PM_WA;\r
1926     case WhiteMarshall:\r
1927         return PM_WC;\r
1928     case WhiteFerz:\r
1929         return PM_WF;\r
1930     case WhiteNightrider:\r
1931         return PM_WH;\r
1932     case WhiteAlfil:\r
1933         return PM_WE;\r
1934     case WhiteWazir:\r
1935         return PM_WW;\r
1936     case WhiteUnicorn:\r
1937         return PM_WU;\r
1938     case WhiteCannon:\r
1939         return PM_WO;\r
1940     case WhiteGrasshopper:\r
1941         return PM_WG;\r
1942     case WhiteMan:\r
1943         return PM_WM;\r
1944     case WhiteSilver:\r
1945         return PM_WSG;\r
1946     case WhiteLance:\r
1947         return PM_WL;\r
1948     case WhiteFalcon:\r
1949         return PM_WV;\r
1950     case WhiteCobra:\r
1951         return PM_WS;\r
1952     case WhiteCardinal:\r
1953         return PM_WAB;\r
1954     case WhiteDragon:\r
1955         return PM_WD;\r
1956     }\r
1957 \r
1958     return 0;\r
1959 }\r
1960 \r
1961 void CreatePiecesFromFont()\r
1962 {\r
1963     LOGFONT lf;\r
1964     HDC hdc_window = NULL;\r
1965     HDC hdc = NULL;\r
1966     HFONT hfont_old;\r
1967     int fontHeight;\r
1968     int i;\r
1969 \r
1970     if( fontBitmapSquareSize < 0 ) {\r
1971         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
1972         return;\r
1973     }\r
1974 \r
1975     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
1976         fontBitmapSquareSize = -1;\r
1977         return;\r
1978     }\r
1979 \r
1980     if( fontBitmapSquareSize != squareSize ) {\r
1981         hdc_window = GetDC( hwndMain );\r
1982         hdc = CreateCompatibleDC( hdc_window );\r
1983 \r
1984         if( hPieceFont != NULL ) {\r
1985             DeleteObject( hPieceFont );\r
1986         }\r
1987         else {\r
1988             for( i=0; i<=(int)BlackKing; i++ ) {\r
1989                 hPieceMask[i] = NULL;\r
1990                 hPieceFace[i] = NULL;\r
1991             }\r
1992         }\r
1993 \r
1994         fontHeight = 75;\r
1995 \r
1996         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
1997             fontHeight = appData.fontPieceSize;\r
1998         }\r
1999 \r
2000         fontHeight = (fontHeight * squareSize) / 100;\r
2001 \r
2002         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2003         lf.lfWidth = 0;\r
2004         lf.lfEscapement = 0;\r
2005         lf.lfOrientation = 0;\r
2006         lf.lfWeight = FW_NORMAL;\r
2007         lf.lfItalic = 0;\r
2008         lf.lfUnderline = 0;\r
2009         lf.lfStrikeOut = 0;\r
2010         lf.lfCharSet = DEFAULT_CHARSET;\r
2011         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2012         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2013         lf.lfQuality = PROOF_QUALITY;\r
2014         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2015         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2016         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2017 \r
2018         hPieceFont = CreateFontIndirect( &lf );\r
2019 \r
2020         if( hPieceFont == NULL ) {\r
2021             fontBitmapSquareSize = -2;\r
2022         }\r
2023         else {\r
2024             /* Setup font-to-piece character table */\r
2025             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2026                 /* No (or wrong) global settings, try to detect the font */\r
2027                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2028                     /* Alpha */\r
2029                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2030                 }\r
2031                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2032                     /* DiagramTT* family */\r
2033                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2034                 }\r
2035                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2036                     /* Fairy symbols */\r
2037                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2038                 }\r
2039                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2040                     /* Good Companion (Some characters get warped as literal :-( */\r
2041                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2042                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2043                     SetCharTable(pieceToFontChar, s);\r
2044                 }\r
2045                 else {\r
2046                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2047                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2048                 }\r
2049             }\r
2050 \r
2051             /* Create bitmaps */\r
2052             hfont_old = SelectObject( hdc, hPieceFont );\r
2053             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2054                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2055                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2056 \r
2057             SelectObject( hdc, hfont_old );\r
2058 \r
2059             fontBitmapSquareSize = squareSize;\r
2060         }\r
2061     }\r
2062 \r
2063     if( hdc != NULL ) {\r
2064         DeleteDC( hdc );\r
2065     }\r
2066 \r
2067     if( hdc_window != NULL ) {\r
2068         ReleaseDC( hwndMain, hdc_window );\r
2069     }\r
2070 }\r
2071 \r
2072 HBITMAP\r
2073 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2074 {\r
2075   char name[128];\r
2076 \r
2077     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2078   if (gameInfo.event &&\r
2079       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2080       strcmp(name, "k80s") == 0) {\r
2081     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2082   }\r
2083   return LoadBitmap(hinst, name);\r
2084 }\r
2085 \r
2086 \r
2087 /* Insert a color into the program's logical palette\r
2088    structure.  This code assumes the given color is\r
2089    the result of the RGB or PALETTERGB macro, and it\r
2090    knows how those macros work (which is documented).\r
2091 */\r
2092 VOID\r
2093 InsertInPalette(COLORREF color)\r
2094 {\r
2095   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2096 \r
2097   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2098     DisplayFatalError(_("Too many colors"), 0, 1);\r
2099     pLogPal->palNumEntries--;\r
2100     return;\r
2101   }\r
2102 \r
2103   pe->peFlags = (char) 0;\r
2104   pe->peRed = (char) (0xFF & color);\r
2105   pe->peGreen = (char) (0xFF & (color >> 8));\r
2106   pe->peBlue = (char) (0xFF & (color >> 16));\r
2107   return;\r
2108 }\r
2109 \r
2110 \r
2111 VOID\r
2112 InitDrawingColors()\r
2113 {\r
2114   if (pLogPal == NULL) {\r
2115     /* Allocate enough memory for a logical palette with\r
2116      * PALETTESIZE entries and set the size and version fields\r
2117      * of the logical palette structure.\r
2118      */\r
2119     pLogPal = (NPLOGPALETTE)\r
2120       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2121                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2122     pLogPal->palVersion    = 0x300;\r
2123   }\r
2124   pLogPal->palNumEntries = 0;\r
2125 \r
2126   InsertInPalette(lightSquareColor);\r
2127   InsertInPalette(darkSquareColor);\r
2128   InsertInPalette(whitePieceColor);\r
2129   InsertInPalette(blackPieceColor);\r
2130   InsertInPalette(highlightSquareColor);\r
2131   InsertInPalette(premoveHighlightColor);\r
2132 \r
2133   /*  create a logical color palette according the information\r
2134    *  in the LOGPALETTE structure.\r
2135    */\r
2136   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2137 \r
2138   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2139   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2140   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2141   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2142   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2143   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2144   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2145   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2146   /* [AS] Force rendering of the font-based pieces */\r
2147   if( fontBitmapSquareSize > 0 ) {\r
2148     fontBitmapSquareSize = 0;\r
2149   }\r
2150 }\r
2151 \r
2152 \r
2153 int\r
2154 BoardWidth(int boardSize, int n)\r
2155 { /* [HGM] argument n added to allow different width and height */\r
2156   int lineGap = sizeInfo[boardSize].lineGap;\r
2157 \r
2158   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2159       lineGap = appData.overrideLineGap;\r
2160   }\r
2161 \r
2162   return (n + 1) * lineGap +\r
2163           n * sizeInfo[boardSize].squareSize;\r
2164 }\r
2165 \r
2166 /* Respond to board resize by dragging edge */\r
2167 VOID\r
2168 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2169 {\r
2170   BoardSize newSize = NUM_SIZES - 1;\r
2171   static int recurse = 0;\r
2172   if (IsIconic(hwndMain)) return;\r
2173   if (recurse > 0) return;\r
2174   recurse++;\r
2175   while (newSize > 0) {\r
2176         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2177         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2178            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2179     newSize--;\r
2180   } \r
2181   boardSize = newSize;\r
2182   InitDrawingSizes(boardSize, flags);\r
2183   recurse--;\r
2184 }\r
2185 \r
2186 \r
2187 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2188 \r
2189 VOID\r
2190 InitDrawingSizes(BoardSize boardSize, int flags)\r
2191 {\r
2192   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2193   ChessSquare piece;\r
2194   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2195   HDC hdc;\r
2196   SIZE clockSize, messageSize;\r
2197   HFONT oldFont;\r
2198   char buf[MSG_SIZ];\r
2199   char *str;\r
2200   HMENU hmenu = GetMenu(hwndMain);\r
2201   RECT crect, wrect, oldRect;\r
2202   int offby;\r
2203   LOGBRUSH logbrush;\r
2204 \r
2205   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2206   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2207 \r
2208   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2209   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2210 \r
2211   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2212   oldRect.top = wpMain.y;\r
2213   oldRect.right = wpMain.x + wpMain.width;\r
2214   oldRect.bottom = wpMain.y + wpMain.height;\r
2215 \r
2216   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2217   smallLayout = sizeInfo[boardSize].smallLayout;\r
2218   squareSize = sizeInfo[boardSize].squareSize;\r
2219   lineGap = sizeInfo[boardSize].lineGap;\r
2220   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2221 \r
2222   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2223       lineGap = appData.overrideLineGap;\r
2224   }\r
2225 \r
2226   if (tinyLayout != oldTinyLayout) {\r
2227     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2228     if (tinyLayout) {\r
2229       style &= ~WS_SYSMENU;\r
2230       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2231                  "&Minimize\tCtrl+F4");\r
2232     } else {\r
2233       style |= WS_SYSMENU;\r
2234       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2235     }\r
2236     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2237 \r
2238     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2239       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2240         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2241     }\r
2242     DrawMenuBar(hwndMain);\r
2243   }\r
2244 \r
2245   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2246   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2247 \r
2248   /* Get text area sizes */\r
2249   hdc = GetDC(hwndMain);\r
2250   if (appData.clockMode) {\r
2251     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2252   } else {\r
2253     snprintf(buf, MSG_SIZ, _("White"));\r
2254   }\r
2255   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2256   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2257   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2258   str = _("We only care about the height here");\r
2259   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2260   SelectObject(hdc, oldFont);\r
2261   ReleaseDC(hwndMain, hdc);\r
2262 \r
2263   /* Compute where everything goes */\r
2264   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2265         /* [HGM] logo: if either logo is on, reserve space for it */\r
2266         logoHeight =  2*clockSize.cy;\r
2267         leftLogoRect.left   = OUTER_MARGIN;\r
2268         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2269         leftLogoRect.top    = OUTER_MARGIN;\r
2270         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2271 \r
2272         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2273         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2274         rightLogoRect.top    = OUTER_MARGIN;\r
2275         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2276 \r
2277 \r
2278     whiteRect.left = leftLogoRect.right;\r
2279     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2280     whiteRect.top = OUTER_MARGIN;\r
2281     whiteRect.bottom = whiteRect.top + logoHeight;\r
2282 \r
2283     blackRect.right = rightLogoRect.left;\r
2284     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2285     blackRect.top = whiteRect.top;\r
2286     blackRect.bottom = whiteRect.bottom;\r
2287   } else {\r
2288     whiteRect.left = OUTER_MARGIN;\r
2289     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2290     whiteRect.top = OUTER_MARGIN;\r
2291     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2292 \r
2293     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2294     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2295     blackRect.top = whiteRect.top;\r
2296     blackRect.bottom = whiteRect.bottom;\r
2297 \r
2298     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2299   }\r
2300 \r
2301   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2302   if (appData.showButtonBar) {\r
2303     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2304       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2305   } else {\r
2306     messageRect.right = OUTER_MARGIN + boardWidth;\r
2307   }\r
2308   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2309   messageRect.bottom = messageRect.top + messageSize.cy;\r
2310 \r
2311   boardRect.left = OUTER_MARGIN;\r
2312   boardRect.right = boardRect.left + boardWidth;\r
2313   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2314   boardRect.bottom = boardRect.top + boardHeight;\r
2315 \r
2316   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2317   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2318   oldBoardSize = boardSize;\r
2319   oldTinyLayout = tinyLayout;\r
2320   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2321   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2322     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2323   winW *= 1 + twoBoards;\r
2324   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2325   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2326   wpMain.height = winH; //       without disturbing window attachments\r
2327   GetWindowRect(hwndMain, &wrect);\r
2328   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2329                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2330 \r
2331   // [HGM] placement: let attached windows follow size change.\r
2332   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2333   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2334   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2335   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2336   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2337 \r
2338   /* compensate if menu bar wrapped */\r
2339   GetClientRect(hwndMain, &crect);\r
2340   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2341   wpMain.height += offby;\r
2342   switch (flags) {\r
2343   case WMSZ_TOPLEFT:\r
2344     SetWindowPos(hwndMain, NULL, \r
2345                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2346                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2347     break;\r
2348 \r
2349   case WMSZ_TOPRIGHT:\r
2350   case WMSZ_TOP:\r
2351     SetWindowPos(hwndMain, NULL, \r
2352                  wrect.left, wrect.bottom - wpMain.height, \r
2353                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2354     break;\r
2355 \r
2356   case WMSZ_BOTTOMLEFT:\r
2357   case WMSZ_LEFT:\r
2358     SetWindowPos(hwndMain, NULL, \r
2359                  wrect.right - wpMain.width, wrect.top, \r
2360                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2361     break;\r
2362 \r
2363   case WMSZ_BOTTOMRIGHT:\r
2364   case WMSZ_BOTTOM:\r
2365   case WMSZ_RIGHT:\r
2366   default:\r
2367     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2368                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2369     break;\r
2370   }\r
2371 \r
2372   hwndPause = NULL;\r
2373   for (i = 0; i < N_BUTTONS; i++) {\r
2374     if (buttonDesc[i].hwnd != NULL) {\r
2375       DestroyWindow(buttonDesc[i].hwnd);\r
2376       buttonDesc[i].hwnd = NULL;\r
2377     }\r
2378     if (appData.showButtonBar) {\r
2379       buttonDesc[i].hwnd =\r
2380         CreateWindow("BUTTON", buttonDesc[i].label,\r
2381                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2382                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2383                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2384                      (HMENU) buttonDesc[i].id,\r
2385                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2386       if (tinyLayout) {\r
2387         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2388                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2389                     MAKELPARAM(FALSE, 0));\r
2390       }\r
2391       if (buttonDesc[i].id == IDM_Pause)\r
2392         hwndPause = buttonDesc[i].hwnd;\r
2393       buttonDesc[i].wndproc = (WNDPROC)\r
2394         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2395     }\r
2396   }\r
2397   if (gridPen != NULL) DeleteObject(gridPen);\r
2398   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2399   if (premovePen != NULL) DeleteObject(premovePen);\r
2400   if (lineGap != 0) {\r
2401     logbrush.lbStyle = BS_SOLID;\r
2402     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2403     gridPen =\r
2404       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2405                    lineGap, &logbrush, 0, NULL);\r
2406     logbrush.lbColor = highlightSquareColor;\r
2407     highlightPen =\r
2408       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2409                    lineGap, &logbrush, 0, NULL);\r
2410 \r
2411     logbrush.lbColor = premoveHighlightColor; \r
2412     premovePen =\r
2413       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2414                    lineGap, &logbrush, 0, NULL);\r
2415 \r
2416     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2417     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2418       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2419       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2420         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2421       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2422         BOARD_WIDTH * (squareSize + lineGap);\r
2423       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2424     }\r
2425     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2426       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2427       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2428         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2429         lineGap / 2 + (i * (squareSize + lineGap));\r
2430       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2431         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2432       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2433     }\r
2434   }\r
2435 \r
2436   /* [HGM] Licensing requirement */\r
2437 #ifdef GOTHIC\r
2438   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2439 #endif\r
2440 #ifdef FALCON\r
2441   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2442 #endif\r
2443   GothicPopUp( "", VariantNormal);\r
2444 \r
2445 \r
2446 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2447 \r
2448   /* Load piece bitmaps for this board size */\r
2449   for (i=0; i<=2; i++) {\r
2450     for (piece = WhitePawn;\r
2451          (int) piece < (int) BlackPawn;\r
2452          piece = (ChessSquare) ((int) piece + 1)) {\r
2453       if (pieceBitmap[i][piece] != NULL)\r
2454         DeleteObject(pieceBitmap[i][piece]);\r
2455     }\r
2456   }\r
2457 \r
2458   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2459   // Orthodox Chess pieces\r
2460   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2461   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2462   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2463   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2464   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2465   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2466   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2467   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2468   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2469   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2470   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2471   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2472   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2473   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2474   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2475   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2476     // in Shogi, Hijack the unused Queen for Lance\r
2477     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2478     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2479     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2480   } else {\r
2481     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2482     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2483     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2484   }\r
2485 \r
2486   if(squareSize <= 72 && squareSize >= 33) { \r
2487     /* A & C are available in most sizes now */\r
2488     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2489       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2490       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2491       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2492       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2493       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2494       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2495       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2496       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2497       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2498       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2499       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2500       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2501     } else { // Smirf-like\r
2502       if(gameInfo.variant == VariantSChess) {\r
2503         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2504         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2505         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2506       } else {\r
2507         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2508         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2509         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2510       }\r
2511     }\r
2512     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2513       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2514       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2515       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2516     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2517       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2518       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2519       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2520     } else { // WinBoard standard\r
2521       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2522       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2523       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2524     }\r
2525   }\r
2526 \r
2527 \r
2528   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2529     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2530     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2531     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2532     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2533     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2534     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2535     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2536     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2537     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2538     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2539     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2540     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2541     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2542     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2543     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2544     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2545     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2546     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2547     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2548     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2549     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2550     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2551     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2552     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2553     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2554     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2555     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2556     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2557     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2558     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2559 \r
2560     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2561       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2562       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2563       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2564       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2565       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2566       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2567       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2568       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2569       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2570       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2571       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2572       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2573     } else {\r
2574       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2575       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2576       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2577       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2578       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2579       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2580       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2581       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2582       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2583       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2584       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2585       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2586     }\r
2587 \r
2588   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2589     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2590     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2591     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2592     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2593     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2594     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2595     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2596     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2597     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2598     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2599     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2600     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2601     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2602     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2603   }\r
2604 \r
2605 \r
2606   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2607   /* special Shogi support in this size */\r
2608   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2609       for (piece = WhitePawn;\r
2610            (int) piece < (int) BlackPawn;\r
2611            piece = (ChessSquare) ((int) piece + 1)) {\r
2612         if (pieceBitmap[i][piece] != NULL)\r
2613           DeleteObject(pieceBitmap[i][piece]);\r
2614       }\r
2615     }\r
2616   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2617   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2618   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2619   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2620   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2621   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2622   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2623   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2624   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2625   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2626   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2627   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2628   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2629   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2630   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2631   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2632   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2633   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2634   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2635   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2636   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2637   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2638   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2639   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2640   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2641   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2642   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2643   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2644   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2645   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2646   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2647   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2648   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2649   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2650   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2651   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2652   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2653   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2654   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2655   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2656   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2657   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2658   minorSize = 0;\r
2659   }\r
2660 }\r
2661 \r
2662 HBITMAP\r
2663 PieceBitmap(ChessSquare p, int kind)\r
2664 {\r
2665   if ((int) p >= (int) BlackPawn)\r
2666     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2667 \r
2668   return pieceBitmap[kind][(int) p];\r
2669 }\r
2670 \r
2671 /***************************************************************/\r
2672 \r
2673 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2674 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2675 /*\r
2676 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2677 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2678 */\r
2679 \r
2680 VOID\r
2681 SquareToPos(int row, int column, int * x, int * y)\r
2682 {\r
2683   if (flipView) {\r
2684     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2685     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2686   } else {\r
2687     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2688     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2689   }\r
2690 }\r
2691 \r
2692 VOID\r
2693 DrawCoordsOnDC(HDC hdc)\r
2694 {\r
2695   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
2696   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
2697   char str[2] = { NULLCHAR, NULLCHAR };\r
2698   int oldMode, oldAlign, x, y, start, i;\r
2699   HFONT oldFont;\r
2700   HBRUSH oldBrush;\r
2701 \r
2702   if (!appData.showCoords)\r
2703     return;\r
2704 \r
2705   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
2706 \r
2707   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2708   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2709   oldAlign = GetTextAlign(hdc);\r
2710   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2711 \r
2712   y = boardRect.top + lineGap;\r
2713   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2714 \r
2715   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2716   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2717     str[0] = files[start + i];\r
2718     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2719     y += squareSize + lineGap;\r
2720   }\r
2721 \r
2722   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
2723 \r
2724   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2725   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2726     str[0] = ranks[start + i];\r
2727     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2728     x += squareSize + lineGap;\r
2729   }    \r
2730 \r
2731   SelectObject(hdc, oldBrush);\r
2732   SetBkMode(hdc, oldMode);\r
2733   SetTextAlign(hdc, oldAlign);\r
2734   SelectObject(hdc, oldFont);\r
2735 }\r
2736 \r
2737 VOID\r
2738 DrawGridOnDC(HDC hdc)\r
2739 {\r
2740   HPEN oldPen;\r
2741  \r
2742   if (lineGap != 0) {\r
2743     oldPen = SelectObject(hdc, gridPen);\r
2744     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2745     SelectObject(hdc, oldPen);\r
2746   }\r
2747 }\r
2748 \r
2749 #define HIGHLIGHT_PEN 0\r
2750 #define PREMOVE_PEN   1\r
2751 \r
2752 VOID\r
2753 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2754 {\r
2755   int x1, y1;\r
2756   HPEN oldPen, hPen;\r
2757   if (lineGap == 0) return;\r
2758   if (flipView) {\r
2759     x1 = boardRect.left +\r
2760       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2761     y1 = boardRect.top +\r
2762       lineGap/2 + y * (squareSize + lineGap);\r
2763   } else {\r
2764     x1 = boardRect.left +\r
2765       lineGap/2 + x * (squareSize + lineGap);\r
2766     y1 = boardRect.top +\r
2767       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2768   }\r
2769   hPen = pen ? premovePen : highlightPen;\r
2770   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2771   MoveToEx(hdc, x1, y1, NULL);\r
2772   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2773   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2774   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2775   LineTo(hdc, x1, y1);\r
2776   SelectObject(hdc, oldPen);\r
2777 }\r
2778 \r
2779 VOID\r
2780 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2781 {\r
2782   int i;\r
2783   for (i=0; i<2; i++) {\r
2784     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2785       DrawHighlightOnDC(hdc, TRUE,\r
2786                         h->sq[i].x, h->sq[i].y,\r
2787                         pen);\r
2788   }\r
2789 }\r
2790 \r
2791 /* Note: sqcolor is used only in monoMode */\r
2792 /* Note that this code is largely duplicated in woptions.c,\r
2793    function DrawSampleSquare, so that needs to be updated too */\r
2794 VOID\r
2795 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2796 {\r
2797   HBITMAP oldBitmap;\r
2798   HBRUSH oldBrush;\r
2799   int tmpSize;\r
2800 \r
2801   if (appData.blindfold) return;\r
2802 \r
2803   /* [AS] Use font-based pieces if needed */\r
2804   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2805     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2806     CreatePiecesFromFont();\r
2807 \r
2808     if( fontBitmapSquareSize == squareSize ) {\r
2809         int index = TranslatePieceToFontPiece(piece);\r
2810 \r
2811         SelectObject( tmphdc, hPieceMask[ index ] );\r
2812 \r
2813       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2814         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2815       else\r
2816         BitBlt( hdc,\r
2817             x, y,\r
2818             squareSize, squareSize,\r
2819             tmphdc,\r
2820             0, 0,\r
2821             SRCAND );\r
2822 \r
2823         SelectObject( tmphdc, hPieceFace[ index ] );\r
2824 \r
2825       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2826         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2827       else\r
2828         BitBlt( hdc,\r
2829             x, y,\r
2830             squareSize, squareSize,\r
2831             tmphdc,\r
2832             0, 0,\r
2833             SRCPAINT );\r
2834 \r
2835         return;\r
2836     }\r
2837   }\r
2838 \r
2839   if (appData.monoMode) {\r
2840     SelectObject(tmphdc, PieceBitmap(piece, \r
2841       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2842     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2843            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2844   } else {\r
2845     tmpSize = squareSize;\r
2846     if(minorSize &&\r
2847         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2848          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2849       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2850       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2851       x += (squareSize - minorSize)>>1;\r
2852       y += squareSize - minorSize - 2;\r
2853       tmpSize = minorSize;\r
2854     }\r
2855     if (color || appData.allWhite ) {\r
2856       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2857       if( color )\r
2858               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2859       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2860       if(appData.upsideDown && color==flipView)\r
2861         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2862       else\r
2863         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2864       /* Use black for outline of white pieces */\r
2865       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2866       if(appData.upsideDown && color==flipView)\r
2867         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2868       else\r
2869         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2870     } else {\r
2871       /* Use square color for details of black pieces */\r
2872       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2873       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2874       if(appData.upsideDown && !flipView)\r
2875         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2876       else\r
2877         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2878     }\r
2879     SelectObject(hdc, oldBrush);\r
2880     SelectObject(tmphdc, oldBitmap);\r
2881   }\r
2882 }\r
2883 \r
2884 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2885 int GetBackTextureMode( int algo )\r
2886 {\r
2887     int result = BACK_TEXTURE_MODE_DISABLED;\r
2888 \r
2889     switch( algo ) \r
2890     {\r
2891         case BACK_TEXTURE_MODE_PLAIN:\r
2892             result = 1; /* Always use identity map */\r
2893             break;\r
2894         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2895             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2896             break;\r
2897     }\r
2898 \r
2899     return result;\r
2900 }\r
2901 \r
2902 /* \r
2903     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2904     to handle redraws cleanly (as random numbers would always be different).\r
2905 */\r
2906 VOID RebuildTextureSquareInfo()\r
2907 {\r
2908     BITMAP bi;\r
2909     int lite_w = 0;\r
2910     int lite_h = 0;\r
2911     int dark_w = 0;\r
2912     int dark_h = 0;\r
2913     int row;\r
2914     int col;\r
2915 \r
2916     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2917 \r
2918     if( liteBackTexture != NULL ) {\r
2919         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2920             lite_w = bi.bmWidth;\r
2921             lite_h = bi.bmHeight;\r
2922         }\r
2923     }\r
2924 \r
2925     if( darkBackTexture != NULL ) {\r
2926         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2927             dark_w = bi.bmWidth;\r
2928             dark_h = bi.bmHeight;\r
2929         }\r
2930     }\r
2931 \r
2932     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2933         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2934             if( (col + row) & 1 ) {\r
2935                 /* Lite square */\r
2936                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2937                   if( lite_w >= squareSize*BOARD_WIDTH )\r
2938                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
2939                   else\r
2940                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2941                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
2942                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
2943                   else\r
2944                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2945                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2946                 }\r
2947             }\r
2948             else {\r
2949                 /* Dark square */\r
2950                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
2951                   if( dark_w >= squareSize*BOARD_WIDTH )\r
2952                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
2953                   else\r
2954                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
2955                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
2956                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
2957                   else\r
2958                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
2959                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
2960                 }\r
2961             }\r
2962         }\r
2963     }\r
2964 }\r
2965 \r
2966 /* [AS] Arrow highlighting support */\r
2967 \r
2968 static double A_WIDTH = 5; /* Width of arrow body */\r
2969 \r
2970 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
2971 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
2972 \r
2973 static double Sqr( double x )\r
2974 {\r
2975     return x*x;\r
2976 }\r
2977 \r
2978 static int Round( double x )\r
2979 {\r
2980     return (int) (x + 0.5);\r
2981 }\r
2982 \r
2983 /* Draw an arrow between two points using current settings */\r
2984 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
2985 {\r
2986     POINT arrow[7];\r
2987     double dx, dy, j, k, x, y;\r
2988 \r
2989     if( d_x == s_x ) {\r
2990         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2991 \r
2992         arrow[0].x = s_x + A_WIDTH + 0.5;\r
2993         arrow[0].y = s_y;\r
2994 \r
2995         arrow[1].x = s_x + A_WIDTH + 0.5;\r
2996         arrow[1].y = d_y - h;\r
2997 \r
2998         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
2999         arrow[2].y = d_y - h;\r
3000 \r
3001         arrow[3].x = d_x;\r
3002         arrow[3].y = d_y;\r
3003 \r
3004         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3005         arrow[5].y = d_y - h;\r
3006 \r
3007         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3008         arrow[4].y = d_y - h;\r
3009 \r
3010         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3011         arrow[6].y = s_y;\r
3012     }\r
3013     else if( d_y == s_y ) {\r
3014         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3015 \r
3016         arrow[0].x = s_x;\r
3017         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3018 \r
3019         arrow[1].x = d_x - w;\r
3020         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3021 \r
3022         arrow[2].x = d_x - w;\r
3023         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3024 \r
3025         arrow[3].x = d_x;\r
3026         arrow[3].y = d_y;\r
3027 \r
3028         arrow[5].x = d_x - w;\r
3029         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3030 \r
3031         arrow[4].x = d_x - w;\r
3032         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3033 \r
3034         arrow[6].x = s_x;\r
3035         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3036     }\r
3037     else {\r
3038         /* [AS] Needed a lot of paper for this! :-) */\r
3039         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3040         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3041   \r
3042         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3043 \r
3044         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3045 \r
3046         x = s_x;\r
3047         y = s_y;\r
3048 \r
3049         arrow[0].x = Round(x - j);\r
3050         arrow[0].y = Round(y + j*dx);\r
3051 \r
3052         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3053         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3054 \r
3055         if( d_x > s_x ) {\r
3056             x = (double) d_x - k;\r
3057             y = (double) d_y - k*dy;\r
3058         }\r
3059         else {\r
3060             x = (double) d_x + k;\r
3061             y = (double) d_y + k*dy;\r
3062         }\r
3063 \r
3064         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3065 \r
3066         arrow[6].x = Round(x - j);\r
3067         arrow[6].y = Round(y + j*dx);\r
3068 \r
3069         arrow[2].x = Round(arrow[6].x + 2*j);\r
3070         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3071 \r
3072         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3073         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3074 \r
3075         arrow[4].x = d_x;\r
3076         arrow[4].y = d_y;\r
3077 \r
3078         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3079         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3080     }\r
3081 \r
3082     Polygon( hdc, arrow, 7 );\r
3083 }\r
3084 \r
3085 /* [AS] Draw an arrow between two squares */\r
3086 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3087 {\r
3088     int s_x, s_y, d_x, d_y;\r
3089     HPEN hpen;\r
3090     HPEN holdpen;\r
3091     HBRUSH hbrush;\r
3092     HBRUSH holdbrush;\r
3093     LOGBRUSH stLB;\r
3094 \r
3095     if( s_col == d_col && s_row == d_row ) {\r
3096         return;\r
3097     }\r
3098 \r
3099     /* Get source and destination points */\r
3100     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3101     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3102 \r
3103     if( d_y > s_y ) {\r
3104         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3105     }\r
3106     else if( d_y < s_y ) {\r
3107         d_y += squareSize / 2 + squareSize / 4;\r
3108     }\r
3109     else {\r
3110         d_y += squareSize / 2;\r
3111     }\r
3112 \r
3113     if( d_x > s_x ) {\r
3114         d_x += squareSize / 2 - squareSize / 4;\r
3115     }\r
3116     else if( d_x < s_x ) {\r
3117         d_x += squareSize / 2 + squareSize / 4;\r
3118     }\r
3119     else {\r
3120         d_x += squareSize / 2;\r
3121     }\r
3122 \r
3123     s_x += squareSize / 2;\r
3124     s_y += squareSize / 2;\r
3125 \r
3126     /* Adjust width */\r
3127     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3128 \r
3129     /* Draw */\r
3130     stLB.lbStyle = BS_SOLID;\r
3131     stLB.lbColor = appData.highlightArrowColor;\r
3132     stLB.lbHatch = 0;\r
3133 \r
3134     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3135     holdpen = SelectObject( hdc, hpen );\r
3136     hbrush = CreateBrushIndirect( &stLB );\r
3137     holdbrush = SelectObject( hdc, hbrush );\r
3138 \r
3139     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3140 \r
3141     SelectObject( hdc, holdpen );\r
3142     SelectObject( hdc, holdbrush );\r
3143     DeleteObject( hpen );\r
3144     DeleteObject( hbrush );\r
3145 }\r
3146 \r
3147 BOOL HasHighlightInfo()\r
3148 {\r
3149     BOOL result = FALSE;\r
3150 \r
3151     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3152         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3153     {\r
3154         result = TRUE;\r
3155     }\r
3156 \r
3157     return result;\r
3158 }\r
3159 \r
3160 BOOL IsDrawArrowEnabled()\r
3161 {\r
3162     BOOL result = FALSE;\r
3163 \r
3164     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3165         result = TRUE;\r
3166     }\r
3167 \r
3168     return result;\r
3169 }\r
3170 \r
3171 VOID DrawArrowHighlight( HDC hdc )\r
3172 {\r
3173     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3174         DrawArrowBetweenSquares( hdc,\r
3175             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3176             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3177     }\r
3178 }\r
3179 \r
3180 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3181 {\r
3182     HRGN result = NULL;\r
3183 \r
3184     if( HasHighlightInfo() ) {\r
3185         int x1, y1, x2, y2;\r
3186         int sx, sy, dx, dy;\r
3187 \r
3188         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3189         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3190 \r
3191         sx = MIN( x1, x2 );\r
3192         sy = MIN( y1, y2 );\r
3193         dx = MAX( x1, x2 ) + squareSize;\r
3194         dy = MAX( y1, y2 ) + squareSize;\r
3195 \r
3196         result = CreateRectRgn( sx, sy, dx, dy );\r
3197     }\r
3198 \r
3199     return result;\r
3200 }\r
3201 \r
3202 /*\r
3203     Warning: this function modifies the behavior of several other functions. \r
3204     \r
3205     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3206     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3207     repaint is scattered all over the place, which is not good for features such as\r
3208     "arrow highlighting" that require a full repaint of the board.\r
3209 \r
3210     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3211     user interaction, when speed is not so important) but especially to avoid errors\r
3212     in the displayed graphics.\r
3213 \r
3214     In such patched places, I always try refer to this function so there is a single\r
3215     place to maintain knowledge.\r
3216     \r
3217     To restore the original behavior, just return FALSE unconditionally.\r
3218 */\r
3219 BOOL IsFullRepaintPreferrable()\r
3220 {\r
3221     BOOL result = FALSE;\r
3222 \r
3223     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3224         /* Arrow may appear on the board */\r
3225         result = TRUE;\r
3226     }\r
3227 \r
3228     return result;\r
3229 }\r
3230 \r
3231 /* \r
3232     This function is called by DrawPosition to know whether a full repaint must\r
3233     be forced or not.\r
3234 \r
3235     Only DrawPosition may directly call this function, which makes use of \r
3236     some state information. Other function should call DrawPosition specifying \r
3237     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3238 */\r
3239 BOOL DrawPositionNeedsFullRepaint()\r
3240 {\r
3241     BOOL result = FALSE;\r
3242 \r
3243     /* \r
3244         Probably a slightly better policy would be to trigger a full repaint\r
3245         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3246         but animation is fast enough that it's difficult to notice.\r
3247     */\r
3248     if( animInfo.piece == EmptySquare ) {\r
3249         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3250             result = TRUE;\r
3251         }\r
3252     }\r
3253 \r
3254     return result;\r
3255 }\r
3256 \r
3257 VOID\r
3258 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3259 {\r
3260   int row, column, x, y, square_color, piece_color;\r
3261   ChessSquare piece;\r
3262   HBRUSH oldBrush;\r
3263   HDC texture_hdc = NULL;\r
3264 \r
3265   /* [AS] Initialize background textures if needed */\r
3266   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3267       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3268       if( backTextureSquareSize != squareSize \r
3269        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3270           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3271           backTextureSquareSize = squareSize;\r
3272           RebuildTextureSquareInfo();\r
3273       }\r
3274 \r
3275       texture_hdc = CreateCompatibleDC( hdc );\r
3276   }\r
3277 \r
3278   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3279     for (column = 0; column < BOARD_WIDTH; column++) {\r
3280   \r
3281       SquareToPos(row, column, &x, &y);\r
3282 \r
3283       piece = board[row][column];\r
3284 \r
3285       square_color = ((column + row) % 2) == 1;\r
3286       if( gameInfo.variant == VariantXiangqi ) {\r
3287           square_color = !InPalace(row, column);\r
3288           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3289           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3290       }\r
3291       piece_color = (int) piece < (int) BlackPawn;\r
3292 \r
3293 \r
3294       /* [HGM] holdings file: light square or black */\r
3295       if(column == BOARD_LEFT-2) {\r
3296             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3297                 square_color = 1;\r
3298             else {\r
3299                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3300                 continue;\r
3301             }\r
3302       } else\r
3303       if(column == BOARD_RGHT + 1 ) {\r
3304             if( row < gameInfo.holdingsSize )\r
3305                 square_color = 1;\r
3306             else {\r
3307                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3308                 continue;\r
3309             }\r
3310       }\r
3311       if(column == BOARD_LEFT-1 ) /* left align */\r
3312             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3313       else if( column == BOARD_RGHT) /* right align */\r
3314             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3315       else\r
3316       if (appData.monoMode) {\r
3317         if (piece == EmptySquare) {\r
3318           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3319                  square_color ? WHITENESS : BLACKNESS);\r
3320         } else {\r
3321           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3322         }\r
3323       } \r
3324       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
3325           /* [AS] Draw the square using a texture bitmap */\r
3326           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3327           int r = row, c = column; // [HGM] do not flip board in flipView\r
3328           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3329 \r
3330           DrawTile( x, y, \r
3331               squareSize, squareSize, \r
3332               hdc, \r
3333               texture_hdc,\r
3334               backTextureSquareInfo[r][c].mode,\r
3335               backTextureSquareInfo[r][c].x,\r
3336               backTextureSquareInfo[r][c].y );\r
3337 \r
3338           SelectObject( texture_hdc, hbm );\r
3339 \r
3340           if (piece != EmptySquare) {\r
3341               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3342           }\r
3343       }\r
3344       else {\r
3345         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3346 \r
3347         oldBrush = SelectObject(hdc, brush );\r
3348         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3349         SelectObject(hdc, oldBrush);\r
3350         if (piece != EmptySquare)\r
3351           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3352       }\r
3353     }\r
3354   }\r
3355 \r
3356   if( texture_hdc != NULL ) {\r
3357     DeleteDC( texture_hdc );\r
3358   }\r
3359 }\r
3360 \r
3361 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3362 void fputDW(FILE *f, int x)\r
3363 {\r
3364         fputc(x     & 255, f);\r
3365         fputc(x>>8  & 255, f);\r
3366         fputc(x>>16 & 255, f);\r
3367         fputc(x>>24 & 255, f);\r
3368 }\r
3369 \r
3370 #define MAX_CLIPS 200   /* more than enough */\r
3371 \r
3372 VOID\r
3373 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3374 {\r
3375 //  HBITMAP bufferBitmap;\r
3376   BITMAP bi;\r
3377 //  RECT Rect;\r
3378   HDC tmphdc;\r
3379   HBITMAP hbm;\r
3380   int w = 100, h = 50;\r
3381 \r
3382   if(logo == NULL) return;\r
3383 //  GetClientRect(hwndMain, &Rect);\r
3384 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3385 //                                      Rect.bottom-Rect.top+1);\r
3386   tmphdc = CreateCompatibleDC(hdc);\r
3387   hbm = SelectObject(tmphdc, logo);\r
3388   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3389             w = bi.bmWidth;\r
3390             h = bi.bmHeight;\r
3391   }\r
3392   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3393                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3394   SelectObject(tmphdc, hbm);\r
3395   DeleteDC(tmphdc);\r
3396 }\r
3397 \r
3398 VOID\r
3399 DisplayLogos()\r
3400 {\r
3401   if(logoHeight) {\r
3402         HDC hdc = GetDC(hwndMain);\r
3403         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3404         if(appData.autoLogo) {\r
3405           \r
3406           switch(gameMode) { // pick logos based on game mode\r
3407             case IcsObserving:\r
3408                 whiteLogo = second.programLogo; // ICS logo\r
3409                 blackLogo = second.programLogo;\r
3410             default:\r
3411                 break;\r
3412             case IcsPlayingWhite:\r
3413                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3414                 blackLogo = second.programLogo; // ICS logo\r
3415                 break;\r
3416             case IcsPlayingBlack:\r
3417                 whiteLogo = second.programLogo; // ICS logo\r
3418                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3419                 break;\r
3420             case TwoMachinesPlay:\r
3421                 if(first.twoMachinesColor[0] == 'b') {\r
3422                     whiteLogo = second.programLogo;\r
3423                     blackLogo = first.programLogo;\r
3424                 }\r
3425                 break;\r
3426             case MachinePlaysWhite:\r
3427                 blackLogo = userLogo;\r
3428                 break;\r
3429             case MachinePlaysBlack:\r
3430                 whiteLogo = userLogo;\r
3431                 blackLogo = first.programLogo;\r
3432           }\r
3433         }\r
3434         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3435         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3436         ReleaseDC(hwndMain, hdc);\r
3437   }\r
3438 }\r
3439 \r
3440 static HDC hdcSeek;\r
3441 \r
3442 // [HGM] seekgraph\r
3443 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3444 {\r
3445     POINT stPt;\r
3446     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3447     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3448     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3449     SelectObject( hdcSeek, hp );\r
3450 }\r
3451 \r
3452 // front-end wrapper for drawing functions to do rectangles\r
3453 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3454 {\r
3455     HPEN hp;\r
3456     RECT rc;\r
3457 \r
3458     if (hdcSeek == NULL) {\r
3459     hdcSeek = GetDC(hwndMain);\r
3460       if (!appData.monoMode) {\r
3461         SelectPalette(hdcSeek, hPal, FALSE);\r
3462         RealizePalette(hdcSeek);\r
3463       }\r
3464     }\r
3465     hp = SelectObject( hdcSeek, gridPen );\r
3466     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3467     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3468     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3469     SelectObject( hdcSeek, hp );\r
3470 }\r
3471 \r
3472 // front-end wrapper for putting text in graph\r
3473 void DrawSeekText(char *buf, int x, int y)\r
3474 {\r
3475         SIZE stSize;\r
3476         SetBkMode( hdcSeek, TRANSPARENT );\r
3477         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3478         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3479 }\r
3480 \r
3481 void DrawSeekDot(int x, int y, int color)\r
3482 {\r
3483         int square = color & 0x80;\r
3484         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3485                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3486         color &= 0x7F;\r
3487         if(square)\r
3488             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3489                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3490         else\r
3491             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3492                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3493             SelectObject(hdcSeek, oldBrush);\r
3494 }\r
3495 \r
3496 VOID\r
3497 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3498 {\r
3499   static Board lastReq[2], lastDrawn[2];\r
3500   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3501   static int lastDrawnFlipView = 0;\r
3502   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3503   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3504   HDC tmphdc;\r
3505   HDC hdcmem;\r
3506   HBITMAP bufferBitmap;\r
3507   HBITMAP oldBitmap;\r
3508   RECT Rect;\r
3509   HRGN clips[MAX_CLIPS];\r
3510   ChessSquare dragged_piece = EmptySquare;\r
3511   int nr = twoBoards*partnerUp;\r
3512 \r
3513   /* I'm undecided on this - this function figures out whether a full\r
3514    * repaint is necessary on its own, so there's no real reason to have the\r
3515    * caller tell it that.  I think this can safely be set to FALSE - but\r
3516    * if we trust the callers not to request full repaints unnessesarily, then\r
3517    * we could skip some clipping work.  In other words, only request a full\r
3518    * redraw when the majority of pieces have changed positions (ie. flip, \r
3519    * gamestart and similar)  --Hawk\r
3520    */\r
3521   Boolean fullrepaint = repaint;\r
3522 \r
3523   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3524 \r
3525   if( DrawPositionNeedsFullRepaint() ) {\r
3526       fullrepaint = TRUE;\r
3527   }\r
3528 \r
3529   if (board == NULL) {\r
3530     if (!lastReqValid[nr]) {\r
3531       return;\r
3532     }\r
3533     board = lastReq[nr];\r
3534   } else {\r
3535     CopyBoard(lastReq[nr], board);\r
3536     lastReqValid[nr] = 1;\r
3537   }\r
3538 \r
3539   if (doingSizing) {\r
3540     return;\r
3541   }\r
3542 \r
3543   if (IsIconic(hwndMain)) {\r
3544     return;\r
3545   }\r
3546 \r
3547   if (hdc == NULL) {\r
3548     hdc = GetDC(hwndMain);\r
3549     if (!appData.monoMode) {\r
3550       SelectPalette(hdc, hPal, FALSE);\r
3551       RealizePalette(hdc);\r
3552     }\r
3553     releaseDC = TRUE;\r
3554   } else {\r
3555     releaseDC = FALSE;\r
3556   }\r
3557 \r
3558   /* Create some work-DCs */\r
3559   hdcmem = CreateCompatibleDC(hdc);\r
3560   tmphdc = CreateCompatibleDC(hdc);\r
3561 \r
3562   /* If dragging is in progress, we temporarely remove the piece */\r
3563   /* [HGM] or temporarily decrease count if stacked              */\r
3564   /*       !! Moved to before board compare !!                   */\r
3565   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3566     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3567     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3568             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3569         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3570     } else \r
3571     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3572             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3573         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3574     } else \r
3575         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3576   }\r
3577 \r
3578   /* Figure out which squares need updating by comparing the \r
3579    * newest board with the last drawn board and checking if\r
3580    * flipping has changed.\r
3581    */\r
3582   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3583     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3584       for (column = 0; column < BOARD_WIDTH; column++) {\r
3585         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3586           SquareToPos(row, column, &x, &y);\r
3587           clips[num_clips++] =\r
3588             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3589         }\r
3590       }\r
3591     }\r
3592    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3593     for (i=0; i<2; i++) {\r
3594       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3595           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3596         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3597             lastDrawnHighlight.sq[i].y >= 0) {\r
3598           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3599                       lastDrawnHighlight.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 (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3605           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3606           clips[num_clips++] =\r
3607             CreateRectRgn(x - lineGap, y - lineGap, \r
3608                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3609         }\r
3610       }\r
3611     }\r
3612     for (i=0; i<2; i++) {\r
3613       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3614           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3615         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3616             lastDrawnPremove.sq[i].y >= 0) {\r
3617           SquareToPos(lastDrawnPremove.sq[i].y,\r
3618                       lastDrawnPremove.sq[i].x, &x, &y);\r
3619           clips[num_clips++] =\r
3620             CreateRectRgn(x - lineGap, y - lineGap, \r
3621                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3622         }\r
3623         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3624             premoveHighlightInfo.sq[i].y >= 0) {\r
3625           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3626                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3627           clips[num_clips++] =\r
3628             CreateRectRgn(x - lineGap, y - lineGap, \r
3629                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3630         }\r
3631       }\r
3632     }\r
3633    } else { // nr == 1\r
3634         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3635         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3636         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3637         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3638       for (i=0; i<2; i++) {\r
3639         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3640             partnerHighlightInfo.sq[i].y >= 0) {\r
3641           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3642                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3643           clips[num_clips++] =\r
3644             CreateRectRgn(x - lineGap, y - lineGap, \r
3645                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3646         }\r
3647         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3648             oldPartnerHighlight.sq[i].y >= 0) {\r
3649           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3650                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3651           clips[num_clips++] =\r
3652             CreateRectRgn(x - lineGap, y - lineGap, \r
3653                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3654         }\r
3655       }\r
3656    }\r
3657   } else {\r
3658     fullrepaint = TRUE;\r
3659   }\r
3660 \r
3661   /* Create a buffer bitmap - this is the actual bitmap\r
3662    * being written to.  When all the work is done, we can\r
3663    * copy it to the real DC (the screen).  This avoids\r
3664    * the problems with flickering.\r
3665    */\r
3666   GetClientRect(hwndMain, &Rect);\r
3667   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3668                                         Rect.bottom-Rect.top+1);\r
3669   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3670   if (!appData.monoMode) {\r
3671     SelectPalette(hdcmem, hPal, FALSE);\r
3672   }\r
3673 \r
3674   /* Create clips for dragging */\r
3675   if (!fullrepaint) {\r
3676     if (dragInfo.from.x >= 0) {\r
3677       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3678       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3679     }\r
3680     if (dragInfo.start.x >= 0) {\r
3681       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3682       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3683     }\r
3684     if (dragInfo.pos.x >= 0) {\r
3685       x = dragInfo.pos.x - squareSize / 2;\r
3686       y = dragInfo.pos.y - squareSize / 2;\r
3687       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3688     }\r
3689     if (dragInfo.lastpos.x >= 0) {\r
3690       x = dragInfo.lastpos.x - squareSize / 2;\r
3691       y = dragInfo.lastpos.y - squareSize / 2;\r
3692       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3693     }\r
3694   }\r
3695 \r
3696   /* Are we animating a move?  \r
3697    * If so, \r
3698    *   - remove the piece from the board (temporarely)\r
3699    *   - calculate the clipping region\r
3700    */\r
3701   if (!fullrepaint) {\r
3702     if (animInfo.piece != EmptySquare) {\r
3703       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3704       x = boardRect.left + animInfo.lastpos.x;\r
3705       y = boardRect.top + animInfo.lastpos.y;\r
3706       x2 = boardRect.left + animInfo.pos.x;\r
3707       y2 = boardRect.top + animInfo.pos.y;\r
3708       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3709       /* Slight kludge.  The real problem is that after AnimateMove is\r
3710          done, the position on the screen does not match lastDrawn.\r
3711          This currently causes trouble only on e.p. captures in\r
3712          atomic, where the piece moves to an empty square and then\r
3713          explodes.  The old and new positions both had an empty square\r
3714          at the destination, but animation has drawn a piece there and\r
3715          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3716       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3717     }\r
3718   }\r
3719 \r
3720   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3721   if (num_clips == 0)\r
3722     fullrepaint = TRUE;\r
3723 \r
3724   /* Set clipping on the memory DC */\r
3725   if (!fullrepaint) {\r
3726     SelectClipRgn(hdcmem, clips[0]);\r
3727     for (x = 1; x < num_clips; x++) {\r
3728       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3729         abort();  // this should never ever happen!\r
3730     }\r
3731   }\r
3732 \r
3733   /* Do all the drawing to the memory DC */\r
3734   if(explodeInfo.radius) { // [HGM] atomic\r
3735         HBRUSH oldBrush;\r
3736         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3737         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3738         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3739         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3740         x += squareSize/2;\r
3741         y += squareSize/2;\r
3742         if(!fullrepaint) {\r
3743           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3744           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3745         }\r
3746         DrawGridOnDC(hdcmem);\r
3747         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3748         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3749         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3750         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3751         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3752         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3753         SelectObject(hdcmem, oldBrush);\r
3754   } else {\r
3755     DrawGridOnDC(hdcmem);\r
3756     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3757         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3758         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3759     } else {\r
3760         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3761         oldPartnerHighlight = partnerHighlightInfo;\r
3762     }\r
3763     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3764   }\r
3765   if(nr == 0) // [HGM] dual: markers only on left board\r
3766   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3767     for (column = 0; column < BOARD_WIDTH; column++) {\r
3768         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3769             HBRUSH oldBrush = SelectObject(hdcmem, \r
3770                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3771             SquareToPos(row, column, &x, &y);\r
3772             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3773                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3774             SelectObject(hdcmem, oldBrush);\r
3775         }\r
3776     }\r
3777   }\r
3778 \r
3779   if( appData.highlightMoveWithArrow ) {\r
3780     DrawArrowHighlight(hdcmem);\r
3781   }\r
3782 \r
3783   DrawCoordsOnDC(hdcmem);\r
3784 \r
3785   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3786                  /* to make sure lastDrawn contains what is actually drawn */\r
3787 \r
3788   /* Put the dragged piece back into place and draw it (out of place!) */\r
3789     if (dragged_piece != EmptySquare) {\r
3790     /* [HGM] or restack */\r
3791     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3792                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3793     else\r
3794     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3795                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3796     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3797     x = dragInfo.pos.x - squareSize / 2;\r
3798     y = dragInfo.pos.y - squareSize / 2;\r
3799     DrawPieceOnDC(hdcmem, dragged_piece,\r
3800                   ((int) dragged_piece < (int) BlackPawn), \r
3801                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3802   }   \r
3803   \r
3804   /* Put the animated piece back into place and draw it */\r
3805   if (animInfo.piece != EmptySquare) {\r
3806     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3807     x = boardRect.left + animInfo.pos.x;\r
3808     y = boardRect.top + animInfo.pos.y;\r
3809     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3810                   ((int) animInfo.piece < (int) BlackPawn),\r
3811                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3812   }\r
3813 \r
3814   /* Release the bufferBitmap by selecting in the old bitmap \r
3815    * and delete the memory DC\r
3816    */\r
3817   SelectObject(hdcmem, oldBitmap);\r
3818   DeleteDC(hdcmem);\r
3819 \r
3820   /* Set clipping on the target DC */\r
3821   if (!fullrepaint) {\r
3822     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3823         RECT rect;\r
3824         GetRgnBox(clips[x], &rect);\r
3825         DeleteObject(clips[x]);\r
3826         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3827                           rect.right + wpMain.width/2, rect.bottom);\r
3828     }\r
3829     SelectClipRgn(hdc, clips[0]);\r
3830     for (x = 1; x < num_clips; x++) {\r
3831       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3832         abort();   // this should never ever happen!\r
3833     } \r
3834   }\r
3835 \r
3836   /* Copy the new bitmap onto the screen in one go.\r
3837    * This way we avoid any flickering\r
3838    */\r
3839   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3840   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3841          boardRect.right - boardRect.left,\r
3842          boardRect.bottom - boardRect.top,\r
3843          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3844   if(saveDiagFlag) { \r
3845     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
3846     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3847 \r
3848     GetObject(bufferBitmap, sizeof(b), &b);\r
3849     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
3850         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3851         bih.biWidth = b.bmWidth;\r
3852         bih.biHeight = b.bmHeight;\r
3853         bih.biPlanes = 1;\r
3854         bih.biBitCount = b.bmBitsPixel;\r
3855         bih.biCompression = 0;\r
3856         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3857         bih.biXPelsPerMeter = 0;\r
3858         bih.biYPelsPerMeter = 0;\r
3859         bih.biClrUsed = 0;\r
3860         bih.biClrImportant = 0;\r
3861 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3862 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3863         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3864 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3865 \r
3866         wb = b.bmWidthBytes;\r
3867         // count colors\r
3868         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3869                 int k = ((int*) pData)[i];\r
3870                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3871                 if(j >= 16) break;\r
3872                 color[j] = k;\r
3873                 if(j >= nrColors) nrColors = j+1;\r
3874         }\r
3875         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3876                 INT p = 0;\r
3877                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3878                     for(w=0; w<(wb>>2); w+=2) {\r
3879                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3880                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3881                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3882                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3883                         pData[p++] = m | j<<4;\r
3884                     }\r
3885                     while(p&3) pData[p++] = 0;\r
3886                 }\r
3887                 fac = 3;\r
3888                 wb = ((wb+31)>>5)<<2;\r
3889         }\r
3890         // write BITMAPFILEHEADER\r
3891         fprintf(diagFile, "BM");\r
3892         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3893         fputDW(diagFile, 0);\r
3894         fputDW(diagFile, 0x36 + (fac?64:0));\r
3895         // write BITMAPINFOHEADER\r
3896         fputDW(diagFile, 40);\r
3897         fputDW(diagFile, b.bmWidth);\r
3898         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3899         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3900         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3901         fputDW(diagFile, 0);\r
3902         fputDW(diagFile, 0);\r
3903         fputDW(diagFile, 0);\r
3904         fputDW(diagFile, 0);\r
3905         fputDW(diagFile, 0);\r
3906         fputDW(diagFile, 0);\r
3907         // write color table\r
3908         if(fac)\r
3909         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3910         // write bitmap data\r
3911         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3912                 fputc(pData[i], diagFile);\r
3913      }\r
3914   }\r
3915 \r
3916   SelectObject(tmphdc, oldBitmap);\r
3917 \r
3918   /* Massive cleanup */\r
3919   for (x = 0; x < num_clips; x++)\r
3920     DeleteObject(clips[x]);\r
3921 \r
3922   DeleteDC(tmphdc);\r
3923   DeleteObject(bufferBitmap);\r
3924 \r
3925   if (releaseDC) \r
3926     ReleaseDC(hwndMain, hdc);\r
3927   \r
3928   if (lastDrawnFlipView != flipView && nr == 0) {\r
3929     if (flipView)\r
3930       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3931     else\r
3932       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3933   }\r
3934 \r
3935 /*  CopyBoard(lastDrawn, board);*/\r
3936   lastDrawnHighlight = highlightInfo;\r
3937   lastDrawnPremove   = premoveHighlightInfo;\r
3938   lastDrawnFlipView = flipView;\r
3939   lastDrawnValid[nr] = 1;\r
3940 }\r
3941 \r
3942 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3943 int\r
3944 SaveDiagram(f)\r
3945      FILE *f;\r
3946 {\r
3947     saveDiagFlag = 1; diagFile = f;\r
3948     HDCDrawPosition(NULL, TRUE, NULL);\r
3949 \r
3950     saveDiagFlag = 0;\r
3951 \r
3952 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
3953     \r
3954     fclose(f);\r
3955     return TRUE;\r
3956 }\r
3957 \r
3958 \r
3959 /*---------------------------------------------------------------------------*\\r
3960 | CLIENT PAINT PROCEDURE\r
3961 |   This is the main event-handler for the WM_PAINT message.\r
3962 |\r
3963 \*---------------------------------------------------------------------------*/\r
3964 VOID\r
3965 PaintProc(HWND hwnd)\r
3966 {\r
3967   HDC         hdc;\r
3968   PAINTSTRUCT ps;\r
3969   HFONT       oldFont;\r
3970 \r
3971   if((hdc = BeginPaint(hwnd, &ps))) {\r
3972     if (IsIconic(hwnd)) {\r
3973       DrawIcon(hdc, 2, 2, iconCurrent);\r
3974     } else {\r
3975       if (!appData.monoMode) {\r
3976         SelectPalette(hdc, hPal, FALSE);\r
3977         RealizePalette(hdc);\r
3978       }\r
3979       HDCDrawPosition(hdc, 1, NULL);\r
3980       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
3981         flipView = !flipView; partnerUp = !partnerUp;\r
3982         HDCDrawPosition(hdc, 1, NULL);\r
3983         flipView = !flipView; partnerUp = !partnerUp;\r
3984       }\r
3985       oldFont =\r
3986         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3987       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
3988                  ETO_CLIPPED|ETO_OPAQUE,\r
3989                  &messageRect, messageText, strlen(messageText), NULL);\r
3990       SelectObject(hdc, oldFont);\r
3991       DisplayBothClocks();\r
3992       DisplayLogos();\r
3993     }\r
3994     EndPaint(hwnd,&ps);\r
3995   }\r
3996 \r
3997   return;\r
3998 }\r
3999 \r
4000 \r
4001 /*\r
4002  * If the user selects on a border boundary, return -1; if off the board,\r
4003  *   return -2.  Otherwise map the event coordinate to the square.\r
4004  * The offset boardRect.left or boardRect.top must already have been\r
4005  *   subtracted from x.\r
4006  */\r
4007 int EventToSquare(x, limit)\r
4008      int x, limit;\r
4009 {\r
4010   if (x <= 0)\r
4011     return -2;\r
4012   if (x < lineGap)\r
4013     return -1;\r
4014   x -= lineGap;\r
4015   if ((x % (squareSize + lineGap)) >= squareSize)\r
4016     return -1;\r
4017   x /= (squareSize + lineGap);\r
4018     if (x >= limit)\r
4019     return -2;\r
4020   return x;\r
4021 }\r
4022 \r
4023 typedef struct {\r
4024   char piece;\r
4025   int command;\r
4026   char* name;\r
4027 } DropEnable;\r
4028 \r
4029 DropEnable dropEnables[] = {\r
4030   { 'P', DP_Pawn, N_("Pawn") },\r
4031   { 'N', DP_Knight, N_("Knight") },\r
4032   { 'B', DP_Bishop, N_("Bishop") },\r
4033   { 'R', DP_Rook, N_("Rook") },\r
4034   { 'Q', DP_Queen, N_("Queen") },\r
4035 };\r
4036 \r
4037 VOID\r
4038 SetupDropMenu(HMENU hmenu)\r
4039 {\r
4040   int i, count, enable;\r
4041   char *p;\r
4042   extern char white_holding[], black_holding[];\r
4043   char item[MSG_SIZ];\r
4044 \r
4045   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4046     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4047                dropEnables[i].piece);\r
4048     count = 0;\r
4049     while (p && *p++ == dropEnables[i].piece) count++;\r
4050       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4051     enable = count > 0 || !appData.testLegality\r
4052       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4053                       && !appData.icsActive);\r
4054     ModifyMenu(hmenu, dropEnables[i].command,\r
4055                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4056                dropEnables[i].command, item);\r
4057   }\r
4058 }\r
4059 \r
4060 void DragPieceBegin(int x, int y)\r
4061 {\r
4062       dragInfo.lastpos.x = boardRect.left + x;\r
4063       dragInfo.lastpos.y = boardRect.top + y;\r
4064       dragInfo.from.x = fromX;\r
4065       dragInfo.from.y = fromY;\r
4066       dragInfo.start = dragInfo.from;\r
4067       SetCapture(hwndMain);\r
4068 }\r
4069 \r
4070 void DragPieceEnd(int x, int y)\r
4071 {\r
4072     ReleaseCapture();\r
4073     dragInfo.start.x = dragInfo.start.y = -1;\r
4074     dragInfo.from = dragInfo.start;\r
4075     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4076 }\r
4077 \r
4078 /* Event handler for mouse messages */\r
4079 VOID\r
4080 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4081 {\r
4082   int x, y, menuNr;\r
4083   POINT pt;\r
4084   static int recursive = 0;\r
4085   HMENU hmenu;\r
4086   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4087 \r
4088   if (recursive) {\r
4089     if (message == WM_MBUTTONUP) {\r
4090       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4091          to the middle button: we simulate pressing the left button too!\r
4092          */\r
4093       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4094       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4095     }\r
4096     return;\r
4097   }\r
4098   recursive++;\r
4099   \r
4100   pt.x = LOWORD(lParam);\r
4101   pt.y = HIWORD(lParam);\r
4102   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4103   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4104   if (!flipView && y >= 0) {\r
4105     y = BOARD_HEIGHT - 1 - y;\r
4106   }\r
4107   if (flipView && x >= 0) {\r
4108     x = BOARD_WIDTH - 1 - x;\r
4109   }\r
4110 \r
4111   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4112 \r
4113   switch (message) {\r
4114   case WM_LBUTTONDOWN:\r
4115       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4116         ClockClick(flipClock);\r
4117       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4118         ClockClick(!flipClock);\r
4119       }\r
4120       dragInfo.start.x = dragInfo.start.y = -1;\r
4121       dragInfo.from = dragInfo.start;\r
4122     if(fromX == -1 && frozen) { // not sure where this is for\r
4123                 fromX = fromY = -1; \r
4124       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4125       break;\r
4126     }\r
4127       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4128       DrawPosition(TRUE, NULL);\r
4129     break;\r
4130 \r
4131   case WM_LBUTTONUP:\r
4132       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4133       DrawPosition(TRUE, NULL);\r
4134     break;\r
4135 \r
4136   case WM_MOUSEMOVE:\r
4137     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4138     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4139     if ((appData.animateDragging || appData.highlightDragging)\r
4140         && (wParam & MK_LBUTTON)\r
4141         && dragInfo.from.x >= 0) \r
4142     {\r
4143       BOOL full_repaint = FALSE;\r
4144 \r
4145       if (appData.animateDragging) {\r
4146         dragInfo.pos = pt;\r
4147       }\r
4148       if (appData.highlightDragging) {\r
4149         SetHighlights(fromX, fromY, x, y);\r
4150         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4151             full_repaint = TRUE;\r
4152         }\r
4153       }\r
4154       \r
4155       DrawPosition( full_repaint, NULL);\r
4156       \r
4157       dragInfo.lastpos = dragInfo.pos;\r
4158     }\r
4159     break;\r
4160 \r
4161   case WM_MOUSEWHEEL: // [DM]\r
4162     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4163        /* Mouse Wheel is being rolled forward\r
4164         * Play moves forward\r
4165         */\r
4166        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4167                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4168        /* Mouse Wheel is being rolled backward\r
4169         * Play moves backward\r
4170         */\r
4171        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4172                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4173     }\r
4174     break;\r
4175 \r
4176   case WM_MBUTTONUP:\r
4177   case WM_RBUTTONUP:\r
4178     ReleaseCapture();\r
4179     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4180     break;\r
4181  \r
4182   case WM_MBUTTONDOWN:\r
4183   case WM_RBUTTONDOWN:\r
4184     ErrorPopDown();\r
4185     ReleaseCapture();\r
4186     fromX = fromY = -1;\r
4187     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4188     dragInfo.start.x = dragInfo.start.y = -1;\r
4189     dragInfo.from = dragInfo.start;\r
4190     dragInfo.lastpos = dragInfo.pos;\r
4191     if (appData.highlightDragging) {\r
4192       ClearHighlights();\r
4193     }\r
4194     if(y == -2) {\r
4195       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4196       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4197           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4198       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4199           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4200       }\r
4201       break;\r
4202     }\r
4203     DrawPosition(TRUE, NULL);\r
4204 \r
4205     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4206     switch (menuNr) {\r
4207     case 0:\r
4208       if (message == WM_MBUTTONDOWN) {\r
4209         buttonCount = 3;  /* even if system didn't think so */\r
4210         if (wParam & MK_SHIFT) \r
4211           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4212         else\r
4213           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4214       } else { /* message == WM_RBUTTONDOWN */\r
4215         /* Just have one menu, on the right button.  Windows users don't\r
4216            think to try the middle one, and sometimes other software steals\r
4217            it, or it doesn't really exist. */\r
4218         if(gameInfo.variant != VariantShogi)\r
4219             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4220         else\r
4221             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4222       }\r
4223       break;\r
4224     case 2:\r
4225       SetCapture(hwndMain);
4226       break;\r
4227     case 1:\r
4228       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4229       SetupDropMenu(hmenu);\r
4230       MenuPopup(hwnd, pt, hmenu, -1);\r
4231     default:\r
4232       break;\r
4233     }\r
4234     break;\r
4235   }\r
4236 \r
4237   recursive--;\r
4238 }\r
4239 \r
4240 /* Preprocess messages for buttons in main window */\r
4241 LRESULT CALLBACK\r
4242 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4243 {\r
4244   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4245   int i, dir;\r
4246 \r
4247   for (i=0; i<N_BUTTONS; i++) {\r
4248     if (buttonDesc[i].id == id) break;\r
4249   }\r
4250   if (i == N_BUTTONS) return 0;\r
4251   switch (message) {\r
4252   case WM_KEYDOWN:\r
4253     switch (wParam) {\r
4254     case VK_LEFT:\r
4255     case VK_RIGHT:\r
4256       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4257       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4258       return TRUE;\r
4259     }\r
4260     break;\r
4261   case WM_CHAR:\r
4262     switch (wParam) {\r
4263     case '\r':\r
4264       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4265       return TRUE;\r
4266     default:\r
4267       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4268         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4269         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4270         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4271         SetFocus(h);\r
4272         SendMessage(h, WM_CHAR, wParam, lParam);\r
4273         return TRUE;\r
4274       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4275         PopUpMoveDialog((char)wParam);\r
4276       }\r
4277       break;\r
4278     }\r
4279     break;\r
4280   }\r
4281   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4282 }\r
4283 \r
4284 /* Process messages for Promotion dialog box */\r
4285 LRESULT CALLBACK\r
4286 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4287 {\r
4288   char promoChar;\r
4289 \r
4290   switch (message) {\r
4291   case WM_INITDIALOG: /* message: initialize dialog box */\r
4292     /* Center the dialog over the application window */\r
4293     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4294     Translate(hDlg, DLG_PromotionKing);\r
4295     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4296       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4297        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4298        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4299                SW_SHOW : SW_HIDE);\r
4300     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4301     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4302        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4303          PieceToChar(WhiteAngel) != '~') ||\r
4304         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4305          PieceToChar(BlackAngel) != '~')   ) ?\r
4306                SW_SHOW : SW_HIDE);\r
4307     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4308        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4309          PieceToChar(WhiteMarshall) != '~') ||\r
4310         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4311          PieceToChar(BlackMarshall) != '~')   ) ?\r
4312                SW_SHOW : SW_HIDE);\r
4313     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4314     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4315        gameInfo.variant != VariantShogi ?\r
4316                SW_SHOW : SW_HIDE);\r
4317     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4318        gameInfo.variant != VariantShogi ?\r
4319                SW_SHOW : SW_HIDE);\r
4320     if(gameInfo.variant == VariantShogi) {\r
4321         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4322         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4323         SetWindowText(hDlg, "Promote?");\r
4324     }\r
4325     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4326        gameInfo.variant == VariantSuper ?\r
4327                SW_SHOW : SW_HIDE);\r
4328     return TRUE;\r
4329 \r
4330   case WM_COMMAND: /* message: received a command */\r
4331     switch (LOWORD(wParam)) {\r
4332     case IDCANCEL:\r
4333       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4334       ClearHighlights();\r
4335       DrawPosition(FALSE, NULL);\r
4336       return TRUE;\r
4337     case PB_King:\r
4338       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4339       break;\r
4340     case PB_Queen:\r
4341       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4342       break;\r
4343     case PB_Rook:\r
4344       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4345       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4346       break;\r
4347     case PB_Bishop:\r
4348       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4349       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4350       break;\r
4351     case PB_Chancellor:\r
4352       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4353       break;\r
4354     case PB_Archbishop:\r
4355       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4356       break;\r
4357     case PB_Knight:\r
4358       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4359       break;\r
4360     default:\r
4361       return FALSE;\r
4362     }\r
4363     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4364     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4365     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4366     fromX = fromY = -1;\r
4367     if (!appData.highlightLastMove) {\r
4368       ClearHighlights();\r
4369       DrawPosition(FALSE, NULL);\r
4370     }\r
4371     return TRUE;\r
4372   }\r
4373   return FALSE;\r
4374 }\r
4375 \r
4376 /* Pop up promotion dialog */\r
4377 VOID\r
4378 PromotionPopup(HWND hwnd)\r
4379 {\r
4380   FARPROC lpProc;\r
4381 \r
4382   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4383   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4384     hwnd, (DLGPROC)lpProc);\r
4385   FreeProcInstance(lpProc);\r
4386 }\r
4387 \r
4388 void\r
4389 PromotionPopUp()\r
4390 {\r
4391   DrawPosition(TRUE, NULL);\r
4392   PromotionPopup(hwndMain);\r
4393 }\r
4394 \r
4395 /* Toggle ShowThinking */\r
4396 VOID\r
4397 ToggleShowThinking()\r
4398 {\r
4399   appData.showThinking = !appData.showThinking;\r
4400   ShowThinkingEvent();\r
4401 }\r
4402 \r
4403 VOID\r
4404 LoadGameDialog(HWND hwnd, char* title)\r
4405 {\r
4406   UINT number = 0;\r
4407   FILE *f;\r
4408   char fileTitle[MSG_SIZ];\r
4409   f = OpenFileDialog(hwnd, "rb", "",\r
4410                      appData.oldSaveStyle ? "gam" : "pgn",\r
4411                      GAME_FILT,\r
4412                      title, &number, fileTitle, NULL);\r
4413   if (f != NULL) {\r
4414     cmailMsgLoaded = FALSE;\r
4415     if (number == 0) {\r
4416       int error = GameListBuild(f);\r
4417       if (error) {\r
4418         DisplayError(_("Cannot build game list"), error);\r
4419       } else if (!ListEmpty(&gameList) &&\r
4420                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4421         GameListPopUp(f, fileTitle);\r
4422         return;\r
4423       }\r
4424       GameListDestroy();\r
4425       number = 1;\r
4426     }\r
4427     LoadGame(f, number, fileTitle, FALSE);\r
4428   }\r
4429 }\r
4430 \r
4431 int get_term_width()\r
4432 {\r
4433     HDC hdc;\r
4434     TEXTMETRIC tm;\r
4435     RECT rc;\r
4436     HFONT hfont, hold_font;\r
4437     LOGFONT lf;\r
4438     HWND hText;\r
4439 \r
4440     if (hwndConsole)\r
4441         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4442     else\r
4443         return 79;\r
4444 \r
4445     // get the text metrics\r
4446     hdc = GetDC(hText);\r
4447     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4448     if (consoleCF.dwEffects & CFE_BOLD)\r
4449         lf.lfWeight = FW_BOLD;\r
4450     if (consoleCF.dwEffects & CFE_ITALIC)\r
4451         lf.lfItalic = TRUE;\r
4452     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4453         lf.lfStrikeOut = TRUE;\r
4454     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4455         lf.lfUnderline = TRUE;\r
4456     hfont = CreateFontIndirect(&lf);\r
4457     hold_font = SelectObject(hdc, hfont);\r
4458     GetTextMetrics(hdc, &tm);\r
4459     SelectObject(hdc, hold_font);\r
4460     DeleteObject(hfont);\r
4461     ReleaseDC(hText, hdc);\r
4462 \r
4463     // get the rectangle\r
4464     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4465 \r
4466     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4467 }\r
4468 \r
4469 void UpdateICSWidth(HWND hText)\r
4470 {\r
4471     LONG old_width, new_width;\r
4472 \r
4473     new_width = get_term_width(hText, FALSE);\r
4474     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4475     if (new_width != old_width)\r
4476     {\r
4477         ics_update_width(new_width);\r
4478         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4479     }\r
4480 }\r
4481 \r
4482 VOID\r
4483 ChangedConsoleFont()\r
4484 {\r
4485   CHARFORMAT cfmt;\r
4486   CHARRANGE tmpsel, sel;\r
4487   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4488   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4489   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4490   PARAFORMAT paraf;\r
4491 \r
4492   cfmt.cbSize = sizeof(CHARFORMAT);\r
4493   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4494     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4495                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4496   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4497    * size.  This was undocumented in the version of MSVC++ that I had\r
4498    * when I wrote the code, but is apparently documented now.\r
4499    */\r
4500   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4501   cfmt.bCharSet = f->lf.lfCharSet;\r
4502   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4503   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4504   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4505   /* Why are the following seemingly needed too? */\r
4506   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4507   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4508   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4509   tmpsel.cpMin = 0;\r
4510   tmpsel.cpMax = -1; /*999999?*/\r
4511   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4512   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4513   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4514    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4515    */\r
4516   paraf.cbSize = sizeof(paraf);\r
4517   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4518   paraf.dxStartIndent = 0;\r
4519   paraf.dxOffset = WRAP_INDENT;\r
4520   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4521   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4522   UpdateICSWidth(hText);\r
4523 }\r
4524 \r
4525 /*---------------------------------------------------------------------------*\\r
4526  *\r
4527  * Window Proc for main window\r
4528  *\r
4529 \*---------------------------------------------------------------------------*/\r
4530 \r
4531 /* Process messages for main window, etc. */\r
4532 LRESULT CALLBACK\r
4533 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4534 {\r
4535   FARPROC lpProc;\r
4536   int wmId, wmEvent;\r
4537   char *defName;\r
4538   FILE *f;\r
4539   UINT number;\r
4540   char fileTitle[MSG_SIZ];\r
4541   char buf[MSG_SIZ];\r
4542   static SnapData sd;\r
4543 \r
4544   switch (message) {\r
4545 \r
4546   case WM_PAINT: /* message: repaint portion of window */\r
4547     PaintProc(hwnd);\r
4548     break;\r
4549 \r
4550   case WM_ERASEBKGND:\r
4551     if (IsIconic(hwnd)) {\r
4552       /* Cheat; change the message */\r
4553       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4554     } else {\r
4555       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4556     }\r
4557     break;\r
4558 \r
4559   case WM_LBUTTONDOWN:\r
4560   case WM_MBUTTONDOWN:\r
4561   case WM_RBUTTONDOWN:\r
4562   case WM_LBUTTONUP:\r
4563   case WM_MBUTTONUP:\r
4564   case WM_RBUTTONUP:\r
4565   case WM_MOUSEMOVE:\r
4566   case WM_MOUSEWHEEL:\r
4567     MouseEvent(hwnd, message, wParam, lParam);\r
4568     break;\r
4569 \r
4570   JAWS_KB_NAVIGATION\r
4571 \r
4572   case WM_CHAR:\r
4573     \r
4574     JAWS_ALT_INTERCEPT\r
4575 \r
4576     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4577         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4578         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4579         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4580         SetFocus(h);\r
4581         SendMessage(h, message, wParam, lParam);\r
4582     } else if(lParam != KF_REPEAT) {\r
4583         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4584                 PopUpMoveDialog((char)wParam);\r
4585         } else if((char)wParam == 003) CopyGameToClipboard();\r
4586          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4587     }\r
4588 \r
4589     break;\r
4590 \r
4591   case WM_PALETTECHANGED:\r
4592     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4593       int nnew;\r
4594       HDC hdc = GetDC(hwndMain);\r
4595       SelectPalette(hdc, hPal, TRUE);\r
4596       nnew = RealizePalette(hdc);\r
4597       if (nnew > 0) {\r
4598         paletteChanged = TRUE;\r
4599         InvalidateRect(hwnd, &boardRect, FALSE);\r
4600       }\r
4601       ReleaseDC(hwnd, hdc);\r
4602     }\r
4603     break;\r
4604 \r
4605   case WM_QUERYNEWPALETTE:\r
4606     if (!appData.monoMode /*&& paletteChanged*/) {\r
4607       int nnew;\r
4608       HDC hdc = GetDC(hwndMain);\r
4609       paletteChanged = FALSE;\r
4610       SelectPalette(hdc, hPal, FALSE);\r
4611       nnew = RealizePalette(hdc);\r
4612       if (nnew > 0) {\r
4613         InvalidateRect(hwnd, &boardRect, FALSE);\r
4614       }\r
4615       ReleaseDC(hwnd, hdc);\r
4616       return TRUE;\r
4617     }\r
4618     return FALSE;\r
4619 \r
4620   case WM_COMMAND: /* message: command from application menu */\r
4621     wmId    = LOWORD(wParam);\r
4622     wmEvent = HIWORD(wParam);\r
4623 \r
4624     switch (wmId) {\r
4625     case IDM_NewGame:\r
4626       ResetGameEvent();\r
4627       SAY("new game enter a move to play against the computer with white");\r
4628       break;\r
4629 \r
4630     case IDM_NewGameFRC:\r
4631       if( NewGameFRC() == 0 ) {\r
4632         ResetGameEvent();\r
4633       }\r
4634       break;\r
4635 \r
4636     case IDM_NewVariant:\r
4637       NewVariantPopup(hwnd);\r
4638       break;\r
4639 \r
4640     case IDM_LoadGame:\r
4641       LoadGameDialog(hwnd, _("Load Game from File"));\r
4642       break;\r
4643 \r
4644     case IDM_LoadNextGame:\r
4645       ReloadGame(1);\r
4646       break;\r
4647 \r
4648     case IDM_LoadPrevGame:\r
4649       ReloadGame(-1);\r
4650       break;\r
4651 \r
4652     case IDM_ReloadGame:\r
4653       ReloadGame(0);\r
4654       break;\r
4655 \r
4656     case IDM_LoadPosition:\r
4657       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4658         Reset(FALSE, TRUE);\r
4659       }\r
4660       number = 1;\r
4661       f = OpenFileDialog(hwnd, "rb", "",\r
4662                          appData.oldSaveStyle ? "pos" : "fen",\r
4663                          POSITION_FILT,\r
4664                          _("Load Position from File"), &number, fileTitle, NULL);\r
4665       if (f != NULL) {\r
4666         LoadPosition(f, number, fileTitle);\r
4667       }\r
4668       break;\r
4669 \r
4670     case IDM_LoadNextPosition:\r
4671       ReloadPosition(1);\r
4672       break;\r
4673 \r
4674     case IDM_LoadPrevPosition:\r
4675       ReloadPosition(-1);\r
4676       break;\r
4677 \r
4678     case IDM_ReloadPosition:\r
4679       ReloadPosition(0);\r
4680       break;\r
4681 \r
4682     case IDM_SaveGame:\r
4683       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4684       f = OpenFileDialog(hwnd, "a", defName,\r
4685                          appData.oldSaveStyle ? "gam" : "pgn",\r
4686                          GAME_FILT,\r
4687                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4688       if (f != NULL) {\r
4689         SaveGame(f, 0, "");\r
4690       }\r
4691       break;\r
4692 \r
4693     case IDM_SavePosition:\r
4694       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4695       f = OpenFileDialog(hwnd, "a", defName,\r
4696                          appData.oldSaveStyle ? "pos" : "fen",\r
4697                          POSITION_FILT,\r
4698                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4699       if (f != NULL) {\r
4700         SavePosition(f, 0, "");\r
4701       }\r
4702       break;\r
4703 \r
4704     case IDM_SaveDiagram:\r
4705       defName = "diagram";\r
4706       f = OpenFileDialog(hwnd, "wb", defName,\r
4707                          "bmp",\r
4708                          DIAGRAM_FILT,\r
4709                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4710       if (f != NULL) {\r
4711         SaveDiagram(f);\r
4712       }\r
4713       break;\r
4714 \r
4715     case IDM_CopyGame:\r
4716       CopyGameToClipboard();\r
4717       break;\r
4718 \r
4719     case IDM_PasteGame:\r
4720       PasteGameFromClipboard();\r
4721       break;\r
4722 \r
4723     case IDM_CopyGameListToClipboard:\r
4724       CopyGameListToClipboard();\r
4725       break;\r
4726 \r
4727     /* [AS] Autodetect FEN or PGN data */\r
4728     case IDM_PasteAny:\r
4729       PasteGameOrFENFromClipboard();\r
4730       break;\r
4731 \r
4732     /* [AS] Move history */\r
4733     case IDM_ShowMoveHistory:\r
4734         if( MoveHistoryIsUp() ) {\r
4735             MoveHistoryPopDown();\r
4736         }\r
4737         else {\r
4738             MoveHistoryPopUp();\r
4739         }\r
4740         break;\r
4741 \r
4742     /* [AS] Eval graph */\r
4743     case IDM_ShowEvalGraph:\r
4744         if( EvalGraphIsUp() ) {\r
4745             EvalGraphPopDown();\r
4746         }\r
4747         else {\r
4748             EvalGraphPopUp();\r
4749             SetFocus(hwndMain);\r
4750         }\r
4751         break;\r
4752 \r
4753     /* [AS] Engine output */\r
4754     case IDM_ShowEngineOutput:\r
4755         if( EngineOutputIsUp() ) {\r
4756             EngineOutputPopDown();\r
4757         }\r
4758         else {\r
4759             EngineOutputPopUp();\r
4760         }\r
4761         break;\r
4762 \r
4763     /* [AS] User adjudication */\r
4764     case IDM_UserAdjudication_White:\r
4765         UserAdjudicationEvent( +1 );\r
4766         break;\r
4767 \r
4768     case IDM_UserAdjudication_Black:\r
4769         UserAdjudicationEvent( -1 );\r
4770         break;\r
4771 \r
4772     case IDM_UserAdjudication_Draw:\r
4773         UserAdjudicationEvent( 0 );\r
4774         break;\r
4775 \r
4776     /* [AS] Game list options dialog */\r
4777     case IDM_GameListOptions:\r
4778       GameListOptions();\r
4779       break;\r
4780 \r
4781     case IDM_NewChat:\r
4782       ChatPopUp(NULL);\r
4783       break;\r
4784 \r
4785     case IDM_CopyPosition:\r
4786       CopyFENToClipboard();\r
4787       break;\r
4788 \r
4789     case IDM_PastePosition:\r
4790       PasteFENFromClipboard();\r
4791       break;\r
4792 \r
4793     case IDM_MailMove:\r
4794       MailMoveEvent();\r
4795       break;\r
4796 \r
4797     case IDM_ReloadCMailMsg:\r
4798       Reset(TRUE, TRUE);\r
4799       ReloadCmailMsgEvent(FALSE);\r
4800       break;\r
4801 \r
4802     case IDM_Minimize:\r
4803       ShowWindow(hwnd, SW_MINIMIZE);\r
4804       break;\r
4805 \r
4806     case IDM_Exit:\r
4807       ExitEvent(0);\r
4808       break;\r
4809 \r
4810     case IDM_MachineWhite:\r
4811       MachineWhiteEvent();\r
4812       /*\r
4813        * refresh the tags dialog only if it's visible\r
4814        */\r
4815       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4816           char *tags;\r
4817           tags = PGNTags(&gameInfo);\r
4818           TagsPopUp(tags, CmailMsg());\r
4819           free(tags);\r
4820       }\r
4821       SAY("computer starts playing white");\r
4822       break;\r
4823 \r
4824     case IDM_MachineBlack:\r
4825       MachineBlackEvent();\r
4826       /*\r
4827        * refresh the tags dialog only if it's visible\r
4828        */\r
4829       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4830           char *tags;\r
4831           tags = PGNTags(&gameInfo);\r
4832           TagsPopUp(tags, CmailMsg());\r
4833           free(tags);\r
4834       }\r
4835       SAY("computer starts playing black");\r
4836       break;\r
4837 \r
4838     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4839       if(gameMode != BeginningOfGame) { // allow menu item to remain enabled for better mode highligting\r
4840         DisplayError(_("You can only start a match from the initial position."), 0); break;\r
4841       }\r
4842       matchMode = 2;// distinguish from command-line-triggered case (matchMode=1)\r
4843       appData.matchGames = appData.defaultMatchGames;\r
4844       matchGame = 1;\r
4845       first.matchWins = second.matchWins = 0;\r
4846 \r
4847     case IDM_TwoMachines:\r
4848       TwoMachinesEvent();\r
4849       /*\r
4850        * refresh the tags dialog only if it's visible\r
4851        */\r
4852       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4853           char *tags;\r
4854           tags = PGNTags(&gameInfo);\r
4855           TagsPopUp(tags, CmailMsg());\r
4856           free(tags);\r
4857       }\r
4858       SAY("computer starts playing both sides");\r
4859       break;\r
4860 \r
4861     case IDM_AnalysisMode:\r
4862       if (!first.analysisSupport) {\r
4863         snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4864         DisplayError(buf, 0);\r
4865       } else {\r
4866         SAY("analyzing current position");\r
4867         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4868         if (appData.icsActive) {\r
4869                if (gameMode != IcsObserving) {\r
4870                  snprintf(buf, MSG_SIZ, "You are not observing a game");\r
4871                        DisplayError(buf, 0);\r
4872                        /* secure check */\r
4873                        if (appData.icsEngineAnalyze) {\r
4874                                if (appData.debugMode) \r
4875                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4876                                ExitAnalyzeMode();\r
4877                                ModeHighlight();\r
4878                                break;\r
4879                        }\r
4880                        break;\r
4881                } else {\r
4882                        /* if enable, user want disable icsEngineAnalyze */\r
4883                        if (appData.icsEngineAnalyze) {\r
4884                                ExitAnalyzeMode();\r
4885                                ModeHighlight();\r
4886                                break;\r
4887                        }\r
4888                        appData.icsEngineAnalyze = TRUE;\r
4889                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4890                }\r
4891         } \r
4892         if (!appData.showThinking) ToggleShowThinking();\r
4893         AnalyzeModeEvent();\r
4894       }\r
4895       break;\r
4896 \r
4897     case IDM_AnalyzeFile:\r
4898       if (!first.analysisSupport) {\r
4899         char buf[MSG_SIZ];\r
4900           snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4901         DisplayError(buf, 0);\r
4902       } else {\r
4903         if (!appData.showThinking) ToggleShowThinking();\r
4904         AnalyzeFileEvent();\r
4905         LoadGameDialog(hwnd, _("Analyze Game from File"));\r
4906         AnalysisPeriodicEvent(1);\r
4907       }\r
4908       break;\r
4909 \r
4910     case IDM_IcsClient:\r
4911       IcsClientEvent();\r
4912       break;\r
4913 \r
4914     case IDM_EditGame:\r
4915     case IDM_EditGame2:\r
4916       EditGameEvent();\r
4917       SAY("edit game");\r
4918       break;\r
4919 \r
4920     case IDM_EditPosition:\r
4921     case IDM_EditPosition2:\r
4922       EditPositionEvent();\r
4923       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4924       break;\r
4925 \r
4926     case IDM_Training:\r
4927       TrainingEvent();\r
4928       break;\r
4929 \r
4930     case IDM_ShowGameList:\r
4931       ShowGameListProc();\r
4932       break;\r
4933 \r
4934     case IDM_EditProgs1:\r
4935       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
4936       break;\r
4937 \r
4938     case IDM_EditProgs2:\r
4939       EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);\r
4940       break;\r
4941 \r
4942     case IDM_EditServers:\r
4943       EditTagsPopUp(icsNames, &icsNames);\r
4944       break;\r
4945 \r
4946     case IDM_EditTags:\r
4947     case IDM_Tags:\r
4948       EditTagsProc();\r
4949       break;\r
4950 \r
4951     case IDM_EditComment:\r
4952     case IDM_Comment:\r
4953       if (commentUp && editComment) {\r
4954         CommentPopDown();\r
4955       } else {\r
4956         EditCommentEvent();\r
4957       }\r
4958       break;\r
4959 \r
4960     case IDM_Pause:\r
4961       PauseEvent();\r
4962       break;\r
4963 \r
4964     case IDM_Accept:\r
4965       AcceptEvent();\r
4966       break;\r
4967 \r
4968     case IDM_Decline:\r
4969       DeclineEvent();\r
4970       break;\r
4971 \r
4972     case IDM_Rematch:\r
4973       RematchEvent();\r
4974       break;\r
4975 \r
4976     case IDM_CallFlag:\r
4977       CallFlagEvent();\r
4978       break;\r
4979 \r
4980     case IDM_Draw:\r
4981       DrawEvent();\r
4982       break;\r
4983 \r
4984     case IDM_Adjourn:\r
4985       AdjournEvent();\r
4986       break;\r
4987 \r
4988     case IDM_Abort:\r
4989       AbortEvent();\r
4990       break;\r
4991 \r
4992     case IDM_Resign:\r
4993       ResignEvent();\r
4994       break;\r
4995 \r
4996     case IDM_StopObserving:\r
4997       StopObservingEvent();\r
4998       break;\r
4999 \r
5000     case IDM_StopExamining:\r
5001       StopExaminingEvent();\r
5002       break;\r
5003 \r
5004     case IDM_Upload:\r
5005       UploadGameEvent();\r
5006       break;\r
5007 \r
5008     case IDM_TypeInMove:\r
5009       PopUpMoveDialog('\000');\r
5010       break;\r
5011 \r
5012     case IDM_TypeInName:\r
5013       PopUpNameDialog('\000');\r
5014       break;\r
5015 \r
5016     case IDM_Backward:\r
5017       BackwardEvent();\r
5018       SetFocus(hwndMain);\r
5019       break;\r
5020 \r
5021     JAWS_MENU_ITEMS\r
5022 \r
5023     case IDM_Forward:\r
5024       ForwardEvent();\r
5025       SetFocus(hwndMain);\r
5026       break;\r
5027 \r
5028     case IDM_ToStart:\r
5029       ToStartEvent();\r
5030       SetFocus(hwndMain);\r
5031       break;\r
5032 \r
5033     case IDM_ToEnd:\r
5034       ToEndEvent();\r
5035       SetFocus(hwndMain);\r
5036       break;\r
5037 \r
5038     case IDM_Revert:\r
5039       RevertEvent(FALSE);\r
5040       break;\r
5041 \r
5042     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5043       RevertEvent(TRUE);\r
5044       break;\r
5045 \r
5046     case IDM_TruncateGame:\r
5047       TruncateGameEvent();\r
5048       break;\r
5049 \r
5050     case IDM_MoveNow:\r
5051       MoveNowEvent();\r
5052       break;\r
5053 \r
5054     case IDM_RetractMove:\r
5055       RetractMoveEvent();\r
5056       break;\r
5057 \r
5058     case IDM_FlipView:\r
5059       flipView = !flipView;\r
5060       DrawPosition(FALSE, NULL);\r
5061       break;\r
5062 \r
5063     case IDM_FlipClock:\r
5064       flipClock = !flipClock;\r
5065       DisplayBothClocks();\r
5066       DisplayLogos();\r
5067       break;\r
5068 \r
5069     case IDM_MuteSounds:\r
5070       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5071       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5072                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5073       break;\r
5074 \r
5075     case IDM_GeneralOptions:\r
5076       GeneralOptionsPopup(hwnd);\r
5077       DrawPosition(TRUE, NULL);\r
5078       break;\r
5079 \r
5080     case IDM_BoardOptions:\r
5081       BoardOptionsPopup(hwnd);\r
5082       break;\r
5083 \r
5084     case IDM_EnginePlayOptions:\r
5085       EnginePlayOptionsPopup(hwnd);\r
5086       break;\r
5087 \r
5088     case IDM_Engine1Options:\r
5089       EngineOptionsPopup(hwnd, &first);\r
5090       break;\r
5091 \r
5092     case IDM_Engine2Options:\r
5093       savedHwnd = hwnd;\r
5094       if(WaitForSecond(SettingsMenuIfReady)) break;\r
5095       EngineOptionsPopup(hwnd, &second);\r
5096       break;\r
5097 \r
5098     case IDM_OptionsUCI:\r
5099       UciOptionsPopup(hwnd);\r
5100       break;\r
5101 \r
5102     case IDM_IcsOptions:\r
5103       IcsOptionsPopup(hwnd);\r
5104       break;\r
5105 \r
5106     case IDM_Fonts:\r
5107       FontsOptionsPopup(hwnd);\r
5108       break;\r
5109 \r
5110     case IDM_Sounds:\r
5111       SoundOptionsPopup(hwnd);\r
5112       break;\r
5113 \r
5114     case IDM_CommPort:\r
5115       CommPortOptionsPopup(hwnd);\r
5116       break;\r
5117 \r
5118     case IDM_LoadOptions:\r
5119       LoadOptionsPopup(hwnd);\r
5120       break;\r
5121 \r
5122     case IDM_SaveOptions:\r
5123       SaveOptionsPopup(hwnd);\r
5124       break;\r
5125 \r
5126     case IDM_TimeControl:\r
5127       TimeControlOptionsPopup(hwnd);\r
5128       break;\r
5129 \r
5130     case IDM_SaveSettings:\r
5131       SaveSettings(settingsFileName);\r
5132       break;\r
5133 \r
5134     case IDM_SaveSettingsOnExit:\r
5135       saveSettingsOnExit = !saveSettingsOnExit;\r
5136       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5137                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5138                                          MF_CHECKED : MF_UNCHECKED));\r
5139       break;\r
5140 \r
5141     case IDM_Hint:\r
5142       HintEvent();\r
5143       break;\r
5144 \r
5145     case IDM_Book:\r
5146       BookEvent();\r
5147       break;\r
5148 \r
5149     case IDM_AboutGame:\r
5150       AboutGameEvent();\r
5151       break;\r
5152 \r
5153     case IDM_Debug:\r
5154       appData.debugMode = !appData.debugMode;\r
5155       if (appData.debugMode) {\r
5156         char dir[MSG_SIZ];\r
5157         GetCurrentDirectory(MSG_SIZ, dir);\r
5158         SetCurrentDirectory(installDir);\r
5159         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5160         SetCurrentDirectory(dir);\r
5161         setbuf(debugFP, NULL);\r
5162       } else {\r
5163         fclose(debugFP);\r
5164         debugFP = NULL;\r
5165       }\r
5166       break;\r
5167 \r
5168     case IDM_HELPCONTENTS:\r
5169       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5170           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5171           MessageBox (GetFocus(),\r
5172                     _("Unable to activate help"),\r
5173                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5174       }\r
5175       break;\r
5176 \r
5177     case IDM_HELPSEARCH:\r
5178         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5179             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5180         MessageBox (GetFocus(),\r
5181                     _("Unable to activate help"),\r
5182                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5183       }\r
5184       break;\r
5185 \r
5186     case IDM_HELPHELP:\r
5187       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5188         MessageBox (GetFocus(),\r
5189                     _("Unable to activate help"),\r
5190                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5191       }\r
5192       break;\r
5193 \r
5194     case IDM_ABOUT:\r
5195       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5196       DialogBox(hInst, \r
5197         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5198         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5199       FreeProcInstance(lpProc);\r
5200       break;\r
5201 \r
5202     case IDM_DirectCommand1:\r
5203       AskQuestionEvent(_("Direct Command"),\r
5204                        _("Send to chess program:"), "", "1");\r
5205       break;\r
5206     case IDM_DirectCommand2:\r
5207       AskQuestionEvent(_("Direct Command"),\r
5208                        _("Send to second chess program:"), "", "2");\r
5209       break;\r
5210 \r
5211     case EP_WhitePawn:\r
5212       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5213       fromX = fromY = -1;\r
5214       break;\r
5215 \r
5216     case EP_WhiteKnight:\r
5217       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5218       fromX = fromY = -1;\r
5219       break;\r
5220 \r
5221     case EP_WhiteBishop:\r
5222       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5223       fromX = fromY = -1;\r
5224       break;\r
5225 \r
5226     case EP_WhiteRook:\r
5227       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5228       fromX = fromY = -1;\r
5229       break;\r
5230 \r
5231     case EP_WhiteQueen:\r
5232       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5233       fromX = fromY = -1;\r
5234       break;\r
5235 \r
5236     case EP_WhiteFerz:\r
5237       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5238       fromX = fromY = -1;\r
5239       break;\r
5240 \r
5241     case EP_WhiteWazir:\r
5242       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5243       fromX = fromY = -1;\r
5244       break;\r
5245 \r
5246     case EP_WhiteAlfil:\r
5247       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5248       fromX = fromY = -1;\r
5249       break;\r
5250 \r
5251     case EP_WhiteCannon:\r
5252       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5253       fromX = fromY = -1;\r
5254       break;\r
5255 \r
5256     case EP_WhiteCardinal:\r
5257       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5258       fromX = fromY = -1;\r
5259       break;\r
5260 \r
5261     case EP_WhiteMarshall:\r
5262       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5263       fromX = fromY = -1;\r
5264       break;\r
5265 \r
5266     case EP_WhiteKing:\r
5267       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5268       fromX = fromY = -1;\r
5269       break;\r
5270 \r
5271     case EP_BlackPawn:\r
5272       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5273       fromX = fromY = -1;\r
5274       break;\r
5275 \r
5276     case EP_BlackKnight:\r
5277       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5278       fromX = fromY = -1;\r
5279       break;\r
5280 \r
5281     case EP_BlackBishop:\r
5282       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5283       fromX = fromY = -1;\r
5284       break;\r
5285 \r
5286     case EP_BlackRook:\r
5287       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5288       fromX = fromY = -1;\r
5289       break;\r
5290 \r
5291     case EP_BlackQueen:\r
5292       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5293       fromX = fromY = -1;\r
5294       break;\r
5295 \r
5296     case EP_BlackFerz:\r
5297       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5298       fromX = fromY = -1;\r
5299       break;\r
5300 \r
5301     case EP_BlackWazir:\r
5302       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5303       fromX = fromY = -1;\r
5304       break;\r
5305 \r
5306     case EP_BlackAlfil:\r
5307       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5308       fromX = fromY = -1;\r
5309       break;\r
5310 \r
5311     case EP_BlackCannon:\r
5312       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5313       fromX = fromY = -1;\r
5314       break;\r
5315 \r
5316     case EP_BlackCardinal:\r
5317       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5318       fromX = fromY = -1;\r
5319       break;\r
5320 \r
5321     case EP_BlackMarshall:\r
5322       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5323       fromX = fromY = -1;\r
5324       break;\r
5325 \r
5326     case EP_BlackKing:\r
5327       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5328       fromX = fromY = -1;\r
5329       break;\r
5330 \r
5331     case EP_EmptySquare:\r
5332       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5333       fromX = fromY = -1;\r
5334       break;\r
5335 \r
5336     case EP_ClearBoard:\r
5337       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5338       fromX = fromY = -1;\r
5339       break;\r
5340 \r
5341     case EP_White:\r
5342       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5343       fromX = fromY = -1;\r
5344       break;\r
5345 \r
5346     case EP_Black:\r
5347       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5348       fromX = fromY = -1;\r
5349       break;\r
5350 \r
5351     case EP_Promote:\r
5352       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5353       fromX = fromY = -1;\r
5354       break;\r
5355 \r
5356     case EP_Demote:\r
5357       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5358       fromX = fromY = -1;\r
5359       break;\r
5360 \r
5361     case DP_Pawn:\r
5362       DropMenuEvent(WhitePawn, fromX, fromY);\r
5363       fromX = fromY = -1;\r
5364       break;\r
5365 \r
5366     case DP_Knight:\r
5367       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5368       fromX = fromY = -1;\r
5369       break;\r
5370 \r
5371     case DP_Bishop:\r
5372       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5373       fromX = fromY = -1;\r
5374       break;\r
5375 \r
5376     case DP_Rook:\r
5377       DropMenuEvent(WhiteRook, fromX, fromY);\r
5378       fromX = fromY = -1;\r
5379       break;\r
5380 \r
5381     case DP_Queen:\r
5382       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5383       fromX = fromY = -1;\r
5384       break;\r
5385 \r
5386     case IDM_English:\r
5387       barbaric = 0; appData.language = "";\r
5388       TranslateMenus(0);\r
5389       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5390       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5391       lastChecked = wmId;\r
5392       break;\r
5393 \r
5394     default:\r
5395       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5396           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5397           TranslateMenus(0);\r
5398           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5399           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5400           lastChecked = wmId;\r
5401           break;\r
5402       }\r
5403       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5404     }\r
5405     break;\r
5406 \r
5407   case WM_TIMER:\r
5408     switch (wParam) {\r
5409     case CLOCK_TIMER_ID:\r
5410       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5411       clockTimerEvent = 0;\r
5412       DecrementClocks(); /* call into back end */\r
5413       break;\r
5414     case LOAD_GAME_TIMER_ID:\r
5415       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5416       loadGameTimerEvent = 0;\r
5417       AutoPlayGameLoop(); /* call into back end */\r
5418       break;\r
5419     case ANALYSIS_TIMER_ID:\r
5420       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5421                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5422         AnalysisPeriodicEvent(0);\r
5423       } else {\r
5424         KillTimer(hwnd, analysisTimerEvent);\r
5425         analysisTimerEvent = 0;\r
5426       }\r
5427       break;\r
5428     case DELAYED_TIMER_ID:\r
5429       KillTimer(hwnd, delayedTimerEvent);\r
5430       delayedTimerEvent = 0;\r
5431       delayedTimerCallback();\r
5432       break;\r
5433     }\r
5434     break;\r
5435 \r
5436   case WM_USER_Input:\r
5437     InputEvent(hwnd, message, wParam, lParam);\r
5438     break;\r
5439 \r
5440   /* [AS] Also move "attached" child windows */\r
5441   case WM_WINDOWPOSCHANGING:\r
5442 \r
5443     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5444         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5445 \r
5446         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5447             /* Window is moving */\r
5448             RECT rcMain;\r
5449 \r
5450 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5451             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5452             rcMain.right  = wpMain.x + wpMain.width;\r
5453             rcMain.top    = wpMain.y;\r
5454             rcMain.bottom = wpMain.y + wpMain.height;\r
5455             \r
5456             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5457             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5458             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5459             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5460             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5461             wpMain.x = lpwp->x;\r
5462             wpMain.y = lpwp->y;\r
5463         }\r
5464     }\r
5465     break;\r
5466 \r
5467   /* [AS] Snapping */\r
5468   case WM_ENTERSIZEMOVE:\r
5469     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5470     if (hwnd == hwndMain) {\r
5471       doingSizing = TRUE;\r
5472       lastSizing = 0;\r
5473     }\r
5474     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5475     break;\r
5476 \r
5477   case WM_SIZING:\r
5478     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5479     if (hwnd == hwndMain) {\r
5480       lastSizing = wParam;\r
5481     }\r
5482     break;\r
5483 \r
5484   case WM_MOVING:\r
5485     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5486       return OnMoving( &sd, hwnd, wParam, lParam );\r
5487 \r
5488   case WM_EXITSIZEMOVE:\r
5489     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5490     if (hwnd == hwndMain) {\r
5491       RECT client;\r
5492       doingSizing = FALSE;\r
5493       InvalidateRect(hwnd, &boardRect, FALSE);\r
5494       GetClientRect(hwnd, &client);\r
5495       ResizeBoard(client.right, client.bottom, lastSizing);\r
5496       lastSizing = 0;\r
5497       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5498     }\r
5499     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5500     break;\r
5501 \r
5502   case WM_DESTROY: /* message: window being destroyed */\r
5503     PostQuitMessage(0);\r
5504     break;\r
5505 \r
5506   case WM_CLOSE:\r
5507     if (hwnd == hwndMain) {\r
5508       ExitEvent(0);\r
5509     }\r
5510     break;\r
5511 \r
5512   default:      /* Passes it on if unprocessed */\r
5513     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5514   }\r
5515   return 0;\r
5516 }\r
5517 \r
5518 /*---------------------------------------------------------------------------*\\r
5519  *\r
5520  * Misc utility routines\r
5521  *\r
5522 \*---------------------------------------------------------------------------*/\r
5523 \r
5524 /*\r
5525  * Decent random number generator, at least not as bad as Windows\r
5526  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5527  */\r
5528 unsigned int randstate;\r
5529 \r
5530 int\r
5531 myrandom(void)\r
5532 {\r
5533   randstate = randstate * 1664525 + 1013904223;\r
5534   return (int) randstate & 0x7fffffff;\r
5535 }\r
5536 \r
5537 void\r
5538 mysrandom(unsigned int seed)\r
5539 {\r
5540   randstate = seed;\r
5541 }\r
5542 \r
5543 \r
5544 /* \r
5545  * returns TRUE if user selects a different color, FALSE otherwise \r
5546  */\r
5547 \r
5548 BOOL\r
5549 ChangeColor(HWND hwnd, COLORREF *which)\r
5550 {\r
5551   static BOOL firstTime = TRUE;\r
5552   static DWORD customColors[16];\r
5553   CHOOSECOLOR cc;\r
5554   COLORREF newcolor;\r
5555   int i;\r
5556   ColorClass ccl;\r
5557 \r
5558   if (firstTime) {\r
5559     /* Make initial colors in use available as custom colors */\r
5560     /* Should we put the compiled-in defaults here instead? */\r
5561     i = 0;\r
5562     customColors[i++] = lightSquareColor & 0xffffff;\r
5563     customColors[i++] = darkSquareColor & 0xffffff;\r
5564     customColors[i++] = whitePieceColor & 0xffffff;\r
5565     customColors[i++] = blackPieceColor & 0xffffff;\r
5566     customColors[i++] = highlightSquareColor & 0xffffff;\r
5567     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5568 \r
5569     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5570       customColors[i++] = textAttribs[ccl].color;\r
5571     }\r
5572     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5573     firstTime = FALSE;\r
5574   }\r
5575 \r
5576   cc.lStructSize = sizeof(cc);\r
5577   cc.hwndOwner = hwnd;\r
5578   cc.hInstance = NULL;\r
5579   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5580   cc.lpCustColors = (LPDWORD) customColors;\r
5581   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5582 \r
5583   if (!ChooseColor(&cc)) return FALSE;\r
5584 \r
5585   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5586   if (newcolor == *which) return FALSE;\r
5587   *which = newcolor;\r
5588   return TRUE;\r
5589 \r
5590   /*\r
5591   InitDrawingColors();\r
5592   InvalidateRect(hwnd, &boardRect, FALSE);\r
5593   */\r
5594 }\r
5595 \r
5596 BOOLEAN\r
5597 MyLoadSound(MySound *ms)\r
5598 {\r
5599   BOOL ok = FALSE;\r
5600   struct stat st;\r
5601   FILE *f;\r
5602 \r
5603   if (ms->data) free(ms->data);\r
5604   ms->data = NULL;\r
5605 \r
5606   switch (ms->name[0]) {\r
5607   case NULLCHAR:\r
5608     /* Silence */\r
5609     ok = TRUE;\r
5610     break;\r
5611   case '$':\r
5612     /* System sound from Control Panel.  Don't preload here. */\r
5613     ok = TRUE;\r
5614     break;\r
5615   case '!':\r
5616     if (ms->name[1] == NULLCHAR) {\r
5617       /* "!" alone = silence */\r
5618       ok = TRUE;\r
5619     } else {\r
5620       /* Builtin wave resource.  Error if not found. */\r
5621       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5622       if (h == NULL) break;\r
5623       ms->data = (void *)LoadResource(hInst, h);\r
5624       if (h == NULL) break;\r
5625       ok = TRUE;\r
5626     }\r
5627     break;\r
5628   default:\r
5629     /* .wav file.  Error if not found. */\r
5630     f = fopen(ms->name, "rb");\r
5631     if (f == NULL) break;\r
5632     if (fstat(fileno(f), &st) < 0) break;\r
5633     ms->data = malloc(st.st_size);\r
5634     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5635     fclose(f);\r
5636     ok = TRUE;\r
5637     break;\r
5638   }\r
5639   if (!ok) {\r
5640     char buf[MSG_SIZ];\r
5641       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5642     DisplayError(buf, GetLastError());\r
5643   }\r
5644   return ok;\r
5645 }\r
5646 \r
5647 BOOLEAN\r
5648 MyPlaySound(MySound *ms)\r
5649 {\r
5650   BOOLEAN ok = FALSE;\r
5651 \r
5652   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5653   switch (ms->name[0]) {\r
5654   case NULLCHAR:\r
5655         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5656     /* Silence */\r
5657     ok = TRUE;\r
5658     break;\r
5659   case '$':\r
5660     /* System sound from Control Panel (deprecated feature).\r
5661        "$" alone or an unset sound name gets default beep (still in use). */\r
5662     if (ms->name[1]) {\r
5663       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5664     }\r
5665     if (!ok) ok = MessageBeep(MB_OK);\r
5666     break; \r
5667   case '!':\r
5668     /* Builtin wave resource, or "!" alone for silence */\r
5669     if (ms->name[1]) {\r
5670       if (ms->data == NULL) return FALSE;\r
5671       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5672     } else {\r
5673       ok = TRUE;\r
5674     }\r
5675     break;\r
5676   default:\r
5677     /* .wav file.  Error if not found. */\r
5678     if (ms->data == NULL) return FALSE;\r
5679     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5680     break;\r
5681   }\r
5682   /* Don't print an error: this can happen innocently if the sound driver\r
5683      is busy; for instance, if another instance of WinBoard is playing\r
5684      a sound at about the same time. */\r
5685   return ok;\r
5686 }\r
5687 \r
5688 \r
5689 LRESULT CALLBACK\r
5690 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5691 {\r
5692   BOOL ok;\r
5693   OPENFILENAME *ofn;\r
5694   static UINT *number; /* gross that this is static */\r
5695 \r
5696   switch (message) {\r
5697   case WM_INITDIALOG: /* message: initialize dialog box */\r
5698     /* Center the dialog over the application window */\r
5699     ofn = (OPENFILENAME *) lParam;\r
5700     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5701       number = (UINT *) ofn->lCustData;\r
5702       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5703     } else {\r
5704       number = NULL;\r
5705     }\r
5706     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5707     Translate(hDlg, 1536);\r
5708     return FALSE;  /* Allow for further processing */\r
5709 \r
5710   case WM_COMMAND:\r
5711     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5712       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5713     }\r
5714     return FALSE;  /* Allow for further processing */\r
5715   }\r
5716   return FALSE;\r
5717 }\r
5718 \r
5719 UINT APIENTRY\r
5720 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5721 {\r
5722   static UINT *number;\r
5723   OPENFILENAME *ofname;\r
5724   OFNOTIFY *ofnot;\r
5725   switch (uiMsg) {\r
5726   case WM_INITDIALOG:\r
5727     Translate(hdlg, DLG_IndexNumber);\r
5728     ofname = (OPENFILENAME *)lParam;\r
5729     number = (UINT *)(ofname->lCustData);\r
5730     break;\r
5731   case WM_NOTIFY:\r
5732     ofnot = (OFNOTIFY *)lParam;\r
5733     if (ofnot->hdr.code == CDN_FILEOK) {\r
5734       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5735     }\r
5736     break;\r
5737   }\r
5738   return 0;\r
5739 }\r
5740 \r
5741 \r
5742 FILE *\r
5743 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5744                char *nameFilt, char *dlgTitle, UINT *number,\r
5745                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5746 {\r
5747   OPENFILENAME openFileName;\r
5748   char buf1[MSG_SIZ];\r
5749   FILE *f;\r
5750 \r
5751   if (fileName == NULL) fileName = buf1;\r
5752   if (defName == NULL) {\r
5753     safeStrCpy(fileName, "*.", 3 );\r
5754     strcat(fileName, defExt);\r
5755   } else {\r
5756     safeStrCpy(fileName, defName, MSG_SIZ );\r
5757   }\r
5758     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5759   if (number) *number = 0;\r
5760 \r
5761   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5762   openFileName.hwndOwner         = hwnd;\r
5763   openFileName.hInstance         = (HANDLE) hInst;\r
5764   openFileName.lpstrFilter       = nameFilt;\r
5765   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5766   openFileName.nMaxCustFilter    = 0L;\r
5767   openFileName.nFilterIndex      = 1L;\r
5768   openFileName.lpstrFile         = fileName;\r
5769   openFileName.nMaxFile          = MSG_SIZ;\r
5770   openFileName.lpstrFileTitle    = fileTitle;\r
5771   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5772   openFileName.lpstrInitialDir   = NULL;\r
5773   openFileName.lpstrTitle        = dlgTitle;\r
5774   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5775     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5776     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5777     | (oldDialog ? 0 : OFN_EXPLORER);\r
5778   openFileName.nFileOffset       = 0;\r
5779   openFileName.nFileExtension    = 0;\r
5780   openFileName.lpstrDefExt       = defExt;\r
5781   openFileName.lCustData         = (LONG) number;\r
5782   openFileName.lpfnHook          = oldDialog ?\r
5783     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5784   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5785 \r
5786   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5787                         GetOpenFileName(&openFileName)) {\r
5788     /* open the file */\r
5789     f = fopen(openFileName.lpstrFile, write);\r
5790     if (f == NULL) {\r
5791       MessageBox(hwnd, _("File open failed"), NULL,\r
5792                  MB_OK|MB_ICONEXCLAMATION);\r
5793       return NULL;\r
5794     }\r
5795   } else {\r
5796     int err = CommDlgExtendedError();\r
5797     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5798     return FALSE;\r
5799   }\r
5800   return f;\r
5801 }\r
5802 \r
5803 \r
5804 \r
5805 VOID APIENTRY\r
5806 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5807 {\r
5808   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5809 \r
5810   /*\r
5811    * Get the first pop-up menu in the menu template. This is the\r
5812    * menu that TrackPopupMenu displays.\r
5813    */\r
5814   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5815   TranslateOneMenu(10, hmenuTrackPopup);\r
5816 \r
5817   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5818 \r
5819   /*\r
5820    * TrackPopup uses screen coordinates, so convert the\r
5821    * coordinates of the mouse click to screen coordinates.\r
5822    */\r
5823   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5824 \r
5825   /* Draw and track the floating pop-up menu. */\r
5826   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5827                  pt.x, pt.y, 0, hwnd, NULL);\r
5828 \r
5829   /* Destroy the menu.*/\r
5830   DestroyMenu(hmenu);\r
5831 }\r
5832    \r
5833 typedef struct {\r
5834   HWND hDlg, hText;\r
5835   int sizeX, sizeY, newSizeX, newSizeY;\r
5836   HDWP hdwp;\r
5837 } ResizeEditPlusButtonsClosure;\r
5838 \r
5839 BOOL CALLBACK\r
5840 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5841 {\r
5842   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5843   RECT rect;\r
5844   POINT pt;\r
5845 \r
5846   if (hChild == cl->hText) return TRUE;\r
5847   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5848   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5849   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5850   ScreenToClient(cl->hDlg, &pt);\r
5851   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5852     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5853   return TRUE;\r
5854 }\r
5855 \r
5856 /* Resize a dialog that has a (rich) edit field filling most of\r
5857    the top, with a row of buttons below */\r
5858 VOID\r
5859 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5860 {\r
5861   RECT rectText;\r
5862   int newTextHeight, newTextWidth;\r
5863   ResizeEditPlusButtonsClosure cl;\r
5864   \r
5865   /*if (IsIconic(hDlg)) return;*/\r
5866   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5867   \r
5868   cl.hdwp = BeginDeferWindowPos(8);\r
5869 \r
5870   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5871   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5872   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5873   if (newTextHeight < 0) {\r
5874     newSizeY += -newTextHeight;\r
5875     newTextHeight = 0;\r
5876   }\r
5877   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5878     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5879 \r
5880   cl.hDlg = hDlg;\r
5881   cl.hText = hText;\r
5882   cl.sizeX = sizeX;\r
5883   cl.sizeY = sizeY;\r
5884   cl.newSizeX = newSizeX;\r
5885   cl.newSizeY = newSizeY;\r
5886   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5887 \r
5888   EndDeferWindowPos(cl.hdwp);\r
5889 }\r
5890 \r
5891 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5892 {\r
5893     RECT    rChild, rParent;\r
5894     int     wChild, hChild, wParent, hParent;\r
5895     int     wScreen, hScreen, xNew, yNew;\r
5896     HDC     hdc;\r
5897 \r
5898     /* Get the Height and Width of the child window */\r
5899     GetWindowRect (hwndChild, &rChild);\r
5900     wChild = rChild.right - rChild.left;\r
5901     hChild = rChild.bottom - rChild.top;\r
5902 \r
5903     /* Get the Height and Width of the parent window */\r
5904     GetWindowRect (hwndParent, &rParent);\r
5905     wParent = rParent.right - rParent.left;\r
5906     hParent = rParent.bottom - rParent.top;\r
5907 \r
5908     /* Get the display limits */\r
5909     hdc = GetDC (hwndChild);\r
5910     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5911     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5912     ReleaseDC(hwndChild, hdc);\r
5913 \r
5914     /* Calculate new X position, then adjust for screen */\r
5915     xNew = rParent.left + ((wParent - wChild) /2);\r
5916     if (xNew < 0) {\r
5917         xNew = 0;\r
5918     } else if ((xNew+wChild) > wScreen) {\r
5919         xNew = wScreen - wChild;\r
5920     }\r
5921 \r
5922     /* Calculate new Y position, then adjust for screen */\r
5923     if( mode == 0 ) {\r
5924         yNew = rParent.top  + ((hParent - hChild) /2);\r
5925     }\r
5926     else {\r
5927         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5928     }\r
5929 \r
5930     if (yNew < 0) {\r
5931         yNew = 0;\r
5932     } else if ((yNew+hChild) > hScreen) {\r
5933         yNew = hScreen - hChild;\r
5934     }\r
5935 \r
5936     /* Set it, and return */\r
5937     return SetWindowPos (hwndChild, NULL,\r
5938                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5939 }\r
5940 \r
5941 /* Center one window over another */\r
5942 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
5943 {\r
5944     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
5945 }\r
5946 \r
5947 /*---------------------------------------------------------------------------*\\r
5948  *\r
5949  * Startup Dialog functions\r
5950  *\r
5951 \*---------------------------------------------------------------------------*/\r
5952 void\r
5953 InitComboStrings(HANDLE hwndCombo, char **cd)\r
5954 {\r
5955   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5956 \r
5957   while (*cd != NULL) {\r
5958     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
5959     cd++;\r
5960   }\r
5961 }\r
5962 \r
5963 void\r
5964 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
5965 {\r
5966   char buf1[MAX_ARG_LEN];\r
5967   int len;\r
5968 \r
5969   if (str[0] == '@') {\r
5970     FILE* f = fopen(str + 1, "r");\r
5971     if (f == NULL) {\r
5972       DisplayFatalError(str + 1, errno, 2);\r
5973       return;\r
5974     }\r
5975     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
5976     fclose(f);\r
5977     buf1[len] = NULLCHAR;\r
5978     str = buf1;\r
5979   }\r
5980 \r
5981   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5982 \r
5983   for (;;) {\r
5984     char buf[MSG_SIZ];\r
5985     char *end = strchr(str, '\n');\r
5986     if (end == NULL) return;\r
5987     memcpy(buf, str, end - str);\r
5988     buf[end - str] = NULLCHAR;\r
5989     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
5990     str = end + 1;\r
5991   }\r
5992 }\r
5993 \r
5994 void\r
5995 SetStartupDialogEnables(HWND hDlg)\r
5996 {\r
5997   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
5998     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5999     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6000   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6001     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6002   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6003     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6004   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6005     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6006   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6007     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6008     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6009     IsDlgButtonChecked(hDlg, OPT_View));\r
6010 }\r
6011 \r
6012 char *\r
6013 QuoteForFilename(char *filename)\r
6014 {\r
6015   int dquote, space;\r
6016   dquote = strchr(filename, '"') != NULL;\r
6017   space = strchr(filename, ' ') != NULL;\r
6018   if (dquote || space) {\r
6019     if (dquote) {\r
6020       return "'";\r
6021     } else {\r
6022       return "\"";\r
6023     }\r
6024   } else {\r
6025     return "";\r
6026   }\r
6027 }\r
6028 \r
6029 VOID\r
6030 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6031 {\r
6032   char buf[MSG_SIZ];\r
6033   char *q;\r
6034 \r
6035   InitComboStringsFromOption(hwndCombo, nthnames);\r
6036   q = QuoteForFilename(nthcp);\r
6037     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6038   if (*nthdir != NULLCHAR) {\r
6039     q = QuoteForFilename(nthdir);\r
6040       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6041   }\r
6042   if (*nthcp == NULLCHAR) {\r
6043     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6044   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6045     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6046     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6047   }\r
6048 }\r
6049 \r
6050 LRESULT CALLBACK\r
6051 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6052 {\r
6053   char buf[MSG_SIZ];\r
6054   HANDLE hwndCombo;\r
6055   char *p;\r
6056 \r
6057   switch (message) {\r
6058   case WM_INITDIALOG:\r
6059     /* Center the dialog */\r
6060     CenterWindow (hDlg, GetDesktopWindow());\r
6061     Translate(hDlg, DLG_Startup);\r
6062     /* Initialize the dialog items */\r
6063     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6064                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6065                   firstChessProgramNames);\r
6066     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6067                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6068                   secondChessProgramNames);\r
6069     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6070     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6071       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6072     if (*appData.icsHelper != NULLCHAR) {\r
6073       char *q = QuoteForFilename(appData.icsHelper);\r
6074       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6075     }\r
6076     if (*appData.icsHost == NULLCHAR) {\r
6077       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6078       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6079     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6080       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6081       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6082     }\r
6083 \r
6084     if (appData.icsActive) {\r
6085       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6086     }\r
6087     else if (appData.noChessProgram) {\r
6088       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6089     }\r
6090     else {\r
6091       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6092     }\r
6093 \r
6094     SetStartupDialogEnables(hDlg);\r
6095     return TRUE;\r
6096 \r
6097   case WM_COMMAND:\r
6098     switch (LOWORD(wParam)) {\r
6099     case IDOK:\r
6100       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6101         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6102         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6103         p = buf;\r
6104         ParseArgs(StringGet, &p);\r
6105         safeStrCpy(buf, "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6106         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6107         p = buf;\r
6108         ParseArgs(StringGet, &p);\r
6109         appData.noChessProgram = FALSE;\r
6110         appData.icsActive = FALSE;\r
6111       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6112         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6113         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6114         p = buf;\r
6115         ParseArgs(StringGet, &p);\r
6116         if (appData.zippyPlay) {\r
6117           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6118           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6119           p = buf;\r
6120           ParseArgs(StringGet, &p);\r
6121         }\r
6122       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6123         appData.noChessProgram = TRUE;\r
6124         appData.icsActive = FALSE;\r
6125       } else {\r
6126         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6127                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6128         return TRUE;\r
6129       }\r
6130       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6131         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6132         p = buf;\r
6133         ParseArgs(StringGet, &p);\r
6134       }\r
6135       EndDialog(hDlg, TRUE);\r
6136       return TRUE;\r
6137 \r
6138     case IDCANCEL:\r
6139       ExitEvent(0);\r
6140       return TRUE;\r
6141 \r
6142     case IDM_HELPCONTENTS:\r
6143       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6144         MessageBox (GetFocus(),\r
6145                     _("Unable to activate help"),\r
6146                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6147       }\r
6148       break;\r
6149 \r
6150     default:\r
6151       SetStartupDialogEnables(hDlg);\r
6152       break;\r
6153     }\r
6154     break;\r
6155   }\r
6156   return FALSE;\r
6157 }\r
6158 \r
6159 /*---------------------------------------------------------------------------*\\r
6160  *\r
6161  * About box dialog functions\r
6162  *\r
6163 \*---------------------------------------------------------------------------*/\r
6164 \r
6165 /* Process messages for "About" dialog box */\r
6166 LRESULT CALLBACK\r
6167 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6168 {\r
6169   switch (message) {\r
6170   case WM_INITDIALOG: /* message: initialize dialog box */\r
6171     /* Center the dialog over the application window */\r
6172     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6173     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6174     Translate(hDlg, ABOUTBOX);\r
6175     JAWS_COPYRIGHT\r
6176     return (TRUE);\r
6177 \r
6178   case WM_COMMAND: /* message: received a command */\r
6179     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6180         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6181       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6182       return (TRUE);\r
6183     }\r
6184     break;\r
6185   }\r
6186   return (FALSE);\r
6187 }\r
6188 \r
6189 /*---------------------------------------------------------------------------*\\r
6190  *\r
6191  * Comment Dialog functions\r
6192  *\r
6193 \*---------------------------------------------------------------------------*/\r
6194 \r
6195 LRESULT CALLBACK\r
6196 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6197 {\r
6198   static HANDLE hwndText = NULL;\r
6199   int len, newSizeX, newSizeY, flags;\r
6200   static int sizeX, sizeY;\r
6201   char *str;\r
6202   RECT rect;\r
6203   MINMAXINFO *mmi;\r
6204 \r
6205   switch (message) {\r
6206   case WM_INITDIALOG: /* message: initialize dialog box */\r
6207     /* Initialize the dialog items */\r
6208     Translate(hDlg, DLG_EditComment);\r
6209     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6210     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6211     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6212     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6213     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6214     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6215     SetWindowText(hDlg, commentTitle);\r
6216     if (editComment) {\r
6217       SetFocus(hwndText);\r
6218     } else {\r
6219       SetFocus(GetDlgItem(hDlg, IDOK));\r
6220     }\r
6221     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6222                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6223                 MAKELPARAM(FALSE, 0));\r
6224     /* Size and position the dialog */\r
6225     if (!commentDialog) {\r
6226       commentDialog = hDlg;\r
6227       flags = SWP_NOZORDER;\r
6228       GetClientRect(hDlg, &rect);\r
6229       sizeX = rect.right;\r
6230       sizeY = rect.bottom;\r
6231       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6232           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6233         WINDOWPLACEMENT wp;\r
6234         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6235         wp.length = sizeof(WINDOWPLACEMENT);\r
6236         wp.flags = 0;\r
6237         wp.showCmd = SW_SHOW;\r
6238         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6239         wp.rcNormalPosition.left = wpComment.x;\r
6240         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6241         wp.rcNormalPosition.top = wpComment.y;\r
6242         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6243         SetWindowPlacement(hDlg, &wp);\r
6244 \r
6245         GetClientRect(hDlg, &rect);\r
6246         newSizeX = rect.right;\r
6247         newSizeY = rect.bottom;\r
6248         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6249                               newSizeX, newSizeY);\r
6250         sizeX = newSizeX;\r
6251         sizeY = newSizeY;\r
6252       }\r
6253     }\r
6254     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6255     return FALSE;\r
6256 \r
6257   case WM_COMMAND: /* message: received a command */\r
6258     switch (LOWORD(wParam)) {\r
6259     case IDOK:\r
6260       if (editComment) {\r
6261         char *p, *q;\r
6262         /* Read changed options from the dialog box */\r
6263         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6264         len = GetWindowTextLength(hwndText);\r
6265         str = (char *) malloc(len + 1);\r
6266         GetWindowText(hwndText, str, len + 1);\r
6267         p = q = str;\r
6268         while (*q) {\r
6269           if (*q == '\r')\r
6270             q++;\r
6271           else\r
6272             *p++ = *q++;\r
6273         }\r
6274         *p = NULLCHAR;\r
6275         ReplaceComment(commentIndex, str);\r
6276         free(str);\r
6277       }\r
6278       CommentPopDown();\r
6279       return TRUE;\r
6280 \r
6281     case IDCANCEL:\r
6282     case OPT_CancelComment:\r
6283       CommentPopDown();\r
6284       return TRUE;\r
6285 \r
6286     case OPT_ClearComment:\r
6287       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6288       break;\r
6289 \r
6290     case OPT_EditComment:\r
6291       EditCommentEvent();\r
6292       return TRUE;\r
6293 \r
6294     default:\r
6295       break;\r
6296     }\r
6297     break;\r
6298 \r
6299   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6300         if( wParam == OPT_CommentText ) {\r
6301             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6302 \r
6303             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6304                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6305                 POINTL pt;\r
6306                 LRESULT index;\r
6307 \r
6308                 pt.x = LOWORD( lpMF->lParam );\r
6309                 pt.y = HIWORD( lpMF->lParam );\r
6310 \r
6311                 if(lpMF->msg == WM_CHAR) {\r
6312                         CHARRANGE sel;\r
6313                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6314                         index = sel.cpMin;\r
6315                 } else\r
6316                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6317 \r
6318                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6319                 len = GetWindowTextLength(hwndText);\r
6320                 str = (char *) malloc(len + 1);\r
6321                 GetWindowText(hwndText, str, len + 1);\r
6322                 ReplaceComment(commentIndex, str);\r
6323                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6324                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6325                 free(str);\r
6326 \r
6327                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6328                 lpMF->msg = WM_USER;\r
6329 \r
6330                 return TRUE;\r
6331             }\r
6332         }\r
6333         break;\r
6334 \r
6335   case WM_SIZE:\r
6336     newSizeX = LOWORD(lParam);\r
6337     newSizeY = HIWORD(lParam);\r
6338     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6339     sizeX = newSizeX;\r
6340     sizeY = newSizeY;\r
6341     break;\r
6342 \r
6343   case WM_GETMINMAXINFO:\r
6344     /* Prevent resizing window too small */\r
6345     mmi = (MINMAXINFO *) lParam;\r
6346     mmi->ptMinTrackSize.x = 100;\r
6347     mmi->ptMinTrackSize.y = 100;\r
6348     break;\r
6349   }\r
6350   return FALSE;\r
6351 }\r
6352 \r
6353 VOID\r
6354 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6355 {\r
6356   FARPROC lpProc;\r
6357   char *p, *q;\r
6358 \r
6359   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6360 \r
6361   if (str == NULL) str = "";\r
6362   p = (char *) malloc(2 * strlen(str) + 2);\r
6363   q = p;\r
6364   while (*str) {\r
6365     if (*str == '\n') *q++ = '\r';\r
6366     *q++ = *str++;\r
6367   }\r
6368   *q = NULLCHAR;\r
6369   if (commentText != NULL) free(commentText);\r
6370 \r
6371   commentIndex = index;\r
6372   commentTitle = title;\r
6373   commentText = p;\r
6374   editComment = edit;\r
6375 \r
6376   if (commentDialog) {\r
6377     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6378     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6379   } else {\r
6380     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6381     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6382                  hwndMain, (DLGPROC)lpProc);\r
6383     FreeProcInstance(lpProc);\r
6384   }\r
6385   commentUp = TRUE;\r
6386 }\r
6387 \r
6388 \r
6389 /*---------------------------------------------------------------------------*\\r
6390  *\r
6391  * Type-in move dialog functions\r
6392  * \r
6393 \*---------------------------------------------------------------------------*/\r
6394 \r
6395 LRESULT CALLBACK\r
6396 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6397 {\r
6398   char move[MSG_SIZ];\r
6399   HWND hInput;\r
6400   ChessMove moveType;\r
6401   int fromX, fromY, toX, toY;\r
6402   char promoChar;\r
6403 \r
6404   switch (message) {\r
6405   case WM_INITDIALOG:\r
6406     move[0] = (char) lParam;\r
6407     move[1] = NULLCHAR;\r
6408     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6409     Translate(hDlg, DLG_TypeInMove);\r
6410     hInput = GetDlgItem(hDlg, OPT_Move);\r
6411     SetWindowText(hInput, move);\r
6412     SetFocus(hInput);\r
6413     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6414     return FALSE;\r
6415 \r
6416   case WM_COMMAND:\r
6417     switch (LOWORD(wParam)) {\r
6418     case IDOK:
6419 \r
6420       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6421       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6422       { int n; Board board;\r
6423         // [HGM] FENedit\r
6424         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
6425                 EditPositionPasteFEN(move);\r
6426                 EndDialog(hDlg, TRUE);\r
6427                 return TRUE;\r
6428         }\r
6429         // [HGM] movenum: allow move number to be typed in any mode\r
6430         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
6431           ToNrEvent(2*n-1);\r
6432           EndDialog(hDlg, TRUE);\r
6433           return TRUE;\r
6434         }\r
6435       }\r
6436       if (gameMode != EditGame && currentMove != forwardMostMove && \r
6437         gameMode != Training) {\r
6438         DisplayMoveError(_("Displayed move is not current"));\r
6439       } else {\r
6440 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
6441         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6442           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
6443         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
6444         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6445           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
6446           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
6447         } else {\r
6448           DisplayMoveError(_("Could not parse move"));\r
6449         }\r
6450       }\r
6451       EndDialog(hDlg, TRUE);\r
6452       return TRUE;\r
6453     case IDCANCEL:\r
6454       EndDialog(hDlg, FALSE);\r
6455       return TRUE;\r
6456     default:\r
6457       break;\r
6458     }\r
6459     break;\r
6460   }\r
6461   return FALSE;\r
6462 }\r
6463 \r
6464 VOID\r
6465 PopUpMoveDialog(char firstchar)\r
6466 {\r
6467     FARPROC lpProc;\r
6468     \r
6469     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
6470         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
6471         gameMode == AnalyzeMode || gameMode == EditGame || \r
6472         gameMode == EditPosition || gameMode == IcsExamining ||\r
6473         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
6474         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
6475                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
6476                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
6477         gameMode == Training) {\r
6478       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6479       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6480         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6481       FreeProcInstance(lpProc);\r
6482     }\r
6483 }\r
6484 \r
6485 /*---------------------------------------------------------------------------*\\r
6486  *\r
6487  * Type-in name dialog functions\r
6488  * \r
6489 \*---------------------------------------------------------------------------*/\r
6490 \r
6491 LRESULT CALLBACK\r
6492 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6493 {\r
6494   char move[MSG_SIZ];\r
6495   HWND hInput;\r
6496 \r
6497   switch (message) {\r
6498   case WM_INITDIALOG:\r
6499     move[0] = (char) lParam;\r
6500     move[1] = NULLCHAR;\r
6501     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6502     Translate(hDlg, DLG_TypeInName);\r
6503     hInput = GetDlgItem(hDlg, OPT_Name);\r
6504     SetWindowText(hInput, move);\r
6505     SetFocus(hInput);\r
6506     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6507     return FALSE;\r
6508 \r
6509   case WM_COMMAND:\r
6510     switch (LOWORD(wParam)) {\r
6511     case IDOK:\r
6512       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6513       appData.userName = strdup(move);\r
6514       SetUserLogo();\r
6515       SetGameInfo();\r
6516       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6517         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6518         DisplayTitle(move);\r
6519       }\r
6520 \r
6521 \r
6522       EndDialog(hDlg, TRUE);\r
6523       return TRUE;\r
6524     case IDCANCEL:\r
6525       EndDialog(hDlg, FALSE);\r
6526       return TRUE;\r
6527     default:\r
6528       break;\r
6529     }\r
6530     break;\r
6531   }\r
6532   return FALSE;\r
6533 }\r
6534 \r
6535 VOID\r
6536 PopUpNameDialog(char firstchar)\r
6537 {\r
6538     FARPROC lpProc;\r
6539     \r
6540       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6541       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6542         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6543       FreeProcInstance(lpProc);\r
6544 }\r
6545 \r
6546 /*---------------------------------------------------------------------------*\\r
6547  *\r
6548  *  Error dialogs\r
6549  * \r
6550 \*---------------------------------------------------------------------------*/\r
6551 \r
6552 /* Nonmodal error box */\r
6553 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6554                              WPARAM wParam, LPARAM lParam);\r
6555 \r
6556 VOID\r
6557 ErrorPopUp(char *title, char *content)\r
6558 {\r
6559   FARPROC lpProc;\r
6560   char *p, *q;\r
6561   BOOLEAN modal = hwndMain == NULL;\r
6562 \r
6563   p = content;\r
6564   q = errorMessage;\r
6565   while (*p) {\r
6566     if (*p == '\n') {\r
6567       if (modal) {\r
6568         *q++ = ' ';\r
6569         p++;\r
6570       } else {\r
6571         *q++ = '\r';\r
6572         *q++ = *p++;\r
6573       }\r
6574     } else {\r
6575       *q++ = *p++;\r
6576     }\r
6577   }\r
6578   *q = NULLCHAR;\r
6579   strncpy(errorTitle, title, sizeof(errorTitle));\r
6580   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6581   \r
6582   if (modal) {\r
6583     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6584   } else {\r
6585     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6586     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6587                  hwndMain, (DLGPROC)lpProc);\r
6588     FreeProcInstance(lpProc);\r
6589   }\r
6590 }\r
6591 \r
6592 VOID\r
6593 ErrorPopDown()\r
6594 {\r
6595   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6596   if (errorDialog == NULL) return;\r
6597   DestroyWindow(errorDialog);\r
6598   errorDialog = NULL;\r
6599   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6600 }\r
6601 \r
6602 LRESULT CALLBACK\r
6603 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6604 {\r
6605   HANDLE hwndText;\r
6606   RECT rChild;\r
6607 \r
6608   switch (message) {\r
6609   case WM_INITDIALOG:\r
6610     GetWindowRect(hDlg, &rChild);\r
6611 \r
6612     /*\r
6613     SetWindowPos(hDlg, NULL, rChild.left,\r
6614       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6615       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6616     */\r
6617 \r
6618     /* \r
6619         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6620         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6621         and it doesn't work when you resize the dialog.\r
6622         For now, just give it a default position.\r
6623     */\r
6624     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6625     Translate(hDlg, DLG_Error);\r
6626 \r
6627     errorDialog = hDlg;\r
6628     SetWindowText(hDlg, errorTitle);\r
6629     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6630     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6631     return FALSE;\r
6632 \r
6633   case WM_COMMAND:\r
6634     switch (LOWORD(wParam)) {\r
6635     case IDOK:\r
6636     case IDCANCEL:\r
6637       if (errorDialog == hDlg) errorDialog = NULL;\r
6638       DestroyWindow(hDlg);\r
6639       return TRUE;\r
6640 \r
6641     default:\r
6642       break;\r
6643     }\r
6644     break;\r
6645   }\r
6646   return FALSE;\r
6647 }\r
6648 \r
6649 #ifdef GOTHIC\r
6650 HWND gothicDialog = NULL;\r
6651 \r
6652 LRESULT CALLBACK\r
6653 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6654 {\r
6655   HANDLE hwndText;\r
6656   RECT rChild;\r
6657   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6658 \r
6659   switch (message) {\r
6660   case WM_INITDIALOG:\r
6661     GetWindowRect(hDlg, &rChild);\r
6662 \r
6663     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6664                                                              SWP_NOZORDER);\r
6665 \r
6666     /* \r
6667         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6668         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6669         and it doesn't work when you resize the dialog.\r
6670         For now, just give it a default position.\r
6671     */\r
6672     gothicDialog = hDlg;\r
6673     SetWindowText(hDlg, errorTitle);\r
6674     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6675     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6676     return FALSE;\r
6677 \r
6678   case WM_COMMAND:\r
6679     switch (LOWORD(wParam)) {\r
6680     case IDOK:\r
6681     case IDCANCEL:\r
6682       if (errorDialog == hDlg) errorDialog = NULL;\r
6683       DestroyWindow(hDlg);\r
6684       return TRUE;\r
6685 \r
6686     default:\r
6687       break;\r
6688     }\r
6689     break;\r
6690   }\r
6691   return FALSE;\r
6692 }\r
6693 \r
6694 VOID\r
6695 GothicPopUp(char *title, VariantClass variant)\r
6696 {\r
6697   FARPROC lpProc;\r
6698   static char *lastTitle;\r
6699 \r
6700   strncpy(errorTitle, title, sizeof(errorTitle));\r
6701   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6702 \r
6703   if(lastTitle != title && gothicDialog != NULL) {\r
6704     DestroyWindow(gothicDialog);\r
6705     gothicDialog = NULL;\r
6706   }\r
6707   if(variant != VariantNormal && gothicDialog == NULL) {\r
6708     title = lastTitle;\r
6709     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6710     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6711                  hwndMain, (DLGPROC)lpProc);\r
6712     FreeProcInstance(lpProc);\r
6713   }\r
6714 }\r
6715 #endif\r
6716 \r
6717 /*---------------------------------------------------------------------------*\\r
6718  *\r
6719  *  Ics Interaction console functions\r
6720  *\r
6721 \*---------------------------------------------------------------------------*/\r
6722 \r
6723 #define HISTORY_SIZE 64\r
6724 static char *history[HISTORY_SIZE];\r
6725 int histIn = 0, histP = 0;\r
6726 \r
6727 VOID\r
6728 SaveInHistory(char *cmd)\r
6729 {\r
6730   if (history[histIn] != NULL) {\r
6731     free(history[histIn]);\r
6732     history[histIn] = NULL;\r
6733   }\r
6734   if (*cmd == NULLCHAR) return;\r
6735   history[histIn] = StrSave(cmd);\r
6736   histIn = (histIn + 1) % HISTORY_SIZE;\r
6737   if (history[histIn] != NULL) {\r
6738     free(history[histIn]);\r
6739     history[histIn] = NULL;\r
6740   }\r
6741   histP = histIn;\r
6742 }\r
6743 \r
6744 char *\r
6745 PrevInHistory(char *cmd)\r
6746 {\r
6747   int newhp;\r
6748   if (histP == histIn) {\r
6749     if (history[histIn] != NULL) free(history[histIn]);\r
6750     history[histIn] = StrSave(cmd);\r
6751   }\r
6752   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6753   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6754   histP = newhp;\r
6755   return history[histP];\r
6756 }\r
6757 \r
6758 char *\r
6759 NextInHistory()\r
6760 {\r
6761   if (histP == histIn) return NULL;\r
6762   histP = (histP + 1) % HISTORY_SIZE;\r
6763   return history[histP];   \r
6764 }\r
6765 \r
6766 HMENU\r
6767 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6768 {\r
6769   HMENU hmenu, h;\r
6770   int i = 0;\r
6771   hmenu = LoadMenu(hInst, "TextMenu");\r
6772   h = GetSubMenu(hmenu, 0);\r
6773   while (e->item) {\r
6774     if (strcmp(e->item, "-") == 0) {\r
6775       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6776     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6777       int flags = MF_STRING, j = 0;\r
6778       if (e->item[0] == '|') {\r
6779         flags |= MF_MENUBARBREAK;\r
6780         j++;\r
6781       }\r
6782       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6783       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6784     }\r
6785     e++;\r
6786     i++;\r
6787   } \r
6788   return hmenu;\r
6789 }\r
6790 \r
6791 WNDPROC consoleTextWindowProc;\r
6792 \r
6793 void\r
6794 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6795 {\r
6796   char buf[MSG_SIZ], name[MSG_SIZ];\r
6797   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6798   CHARRANGE sel;\r
6799 \r
6800   if (!getname) {\r
6801     SetWindowText(hInput, command);\r
6802     if (immediate) {\r
6803       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6804     } else {\r
6805       sel.cpMin = 999999;\r
6806       sel.cpMax = 999999;\r
6807       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6808       SetFocus(hInput);\r
6809     }\r
6810     return;\r
6811   }    \r
6812   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6813   if (sel.cpMin == sel.cpMax) {\r
6814     /* Expand to surrounding word */\r
6815     TEXTRANGE tr;\r
6816     do {\r
6817       tr.chrg.cpMax = sel.cpMin;\r
6818       tr.chrg.cpMin = --sel.cpMin;\r
6819       if (sel.cpMin < 0) break;\r
6820       tr.lpstrText = name;\r
6821       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6822     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6823     sel.cpMin++;\r
6824 \r
6825     do {\r
6826       tr.chrg.cpMin = sel.cpMax;\r
6827       tr.chrg.cpMax = ++sel.cpMax;\r
6828       tr.lpstrText = name;\r
6829       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6830     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6831     sel.cpMax--;\r
6832 \r
6833     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6834       MessageBeep(MB_ICONEXCLAMATION);\r
6835       return;\r
6836     }\r
6837     tr.chrg = sel;\r
6838     tr.lpstrText = name;\r
6839     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6840   } else {\r
6841     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6842       MessageBeep(MB_ICONEXCLAMATION);\r
6843       return;\r
6844     }\r
6845     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6846   }\r
6847   if (immediate) {\r
6848     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6849     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6850     SetWindowText(hInput, buf);\r
6851     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6852   } else {\r
6853     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6854       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6855     SetWindowText(hInput, buf);\r
6856     sel.cpMin = 999999;\r
6857     sel.cpMax = 999999;\r
6858     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6859     SetFocus(hInput);\r
6860   }\r
6861 }\r
6862 \r
6863 LRESULT CALLBACK \r
6864 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6865 {\r
6866   HWND hInput;\r
6867   CHARRANGE sel;\r
6868 \r
6869   switch (message) {\r
6870   case WM_KEYDOWN:\r
6871     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6872     if(wParam=='R') return 0;\r
6873     switch (wParam) {\r
6874     case VK_PRIOR:\r
6875       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6876       return 0;\r
6877     case VK_NEXT:\r
6878       sel.cpMin = 999999;\r
6879       sel.cpMax = 999999;\r
6880       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6881       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6882       return 0;\r
6883     }\r
6884     break;\r
6885   case WM_CHAR:\r
6886    if(wParam != '\022') {\r
6887     if (wParam == '\t') {\r
6888       if (GetKeyState(VK_SHIFT) < 0) {\r
6889         /* shifted */\r
6890         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6891         if (buttonDesc[0].hwnd) {\r
6892           SetFocus(buttonDesc[0].hwnd);\r
6893         } else {\r
6894           SetFocus(hwndMain);\r
6895         }\r
6896       } else {\r
6897         /* unshifted */\r
6898         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6899       }\r
6900     } else {\r
6901       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6902       JAWS_DELETE( SetFocus(hInput); )\r
6903       SendMessage(hInput, message, wParam, lParam);\r
6904     }\r
6905     return 0;\r
6906    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
6907    lParam = -1;\r
6908   case WM_RBUTTONDOWN:\r
6909     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6910       /* Move selection here if it was empty */\r
6911       POINT pt;\r
6912       pt.x = LOWORD(lParam);\r
6913       pt.y = HIWORD(lParam);\r
6914       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6915       if (sel.cpMin == sel.cpMax) {\r
6916         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6917         sel.cpMax = sel.cpMin;\r
6918         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6919       }\r
6920       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6921 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6922       POINT pt;\r
6923       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6924       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6925       if (sel.cpMin == sel.cpMax) {\r
6926         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6927         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6928       }\r
6929       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6930         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6931       }\r
6932       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6933       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6934       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6935       MenuPopup(hwnd, pt, hmenu, -1);\r
6936 }\r
6937     }\r
6938     return 0;\r
6939   case WM_RBUTTONUP:\r
6940     if (GetKeyState(VK_SHIFT) & ~1) {\r
6941       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6942         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6943     }\r
6944     return 0;\r
6945   case WM_PASTE:\r
6946     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6947     SetFocus(hInput);\r
6948     return SendMessage(hInput, message, wParam, lParam);\r
6949   case WM_MBUTTONDOWN:\r
6950     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6951   case WM_COMMAND:\r
6952     switch (LOWORD(wParam)) {\r
6953     case IDM_QuickPaste:\r
6954       {\r
6955         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6956         if (sel.cpMin == sel.cpMax) {\r
6957           MessageBeep(MB_ICONEXCLAMATION);\r
6958           return 0;\r
6959         }\r
6960         SendMessage(hwnd, WM_COPY, 0, 0);\r
6961         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6962         SendMessage(hInput, WM_PASTE, 0, 0);\r
6963         SetFocus(hInput);\r
6964         return 0;\r
6965       }\r
6966     case IDM_Cut:\r
6967       SendMessage(hwnd, WM_CUT, 0, 0);\r
6968       return 0;\r
6969     case IDM_Paste:\r
6970       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6971       return 0;\r
6972     case IDM_Copy:\r
6973       SendMessage(hwnd, WM_COPY, 0, 0);\r
6974       return 0;\r
6975     default:\r
6976       {\r
6977         int i = LOWORD(wParam) - IDM_CommandX;\r
6978         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6979             icsTextMenuEntry[i].command != NULL) {\r
6980           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6981                    icsTextMenuEntry[i].getname,\r
6982                    icsTextMenuEntry[i].immediate);\r
6983           return 0;\r
6984         }\r
6985       }\r
6986       break;\r
6987     }\r
6988     break;\r
6989   }\r
6990   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
6991 }\r
6992 \r
6993 WNDPROC consoleInputWindowProc;\r
6994 \r
6995 LRESULT CALLBACK\r
6996 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6997 {\r
6998   char buf[MSG_SIZ];\r
6999   char *p;\r
7000   static BOOL sendNextChar = FALSE;\r
7001   static BOOL quoteNextChar = FALSE;\r
7002   InputSource *is = consoleInputSource;\r
7003   CHARFORMAT cf;\r
7004   CHARRANGE sel;\r
7005 \r
7006   switch (message) {\r
7007   case WM_CHAR:\r
7008     if (!appData.localLineEditing || sendNextChar) {\r
7009       is->buf[0] = (CHAR) wParam;\r
7010       is->count = 1;\r
7011       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7012       sendNextChar = FALSE;\r
7013       return 0;\r
7014     }\r
7015     if (quoteNextChar) {\r
7016       buf[0] = (char) wParam;\r
7017       buf[1] = NULLCHAR;\r
7018       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7019       quoteNextChar = FALSE;\r
7020       return 0;\r
7021     }\r
7022     switch (wParam) {\r
7023     case '\r':   /* Enter key */\r
7024       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7025       if (consoleEcho) SaveInHistory(is->buf);\r
7026       is->buf[is->count++] = '\n';\r
7027       is->buf[is->count] = NULLCHAR;\r
7028       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7029       if (consoleEcho) {\r
7030         ConsoleOutput(is->buf, is->count, TRUE);\r
7031       } else if (appData.localLineEditing) {\r
7032         ConsoleOutput("\n", 1, TRUE);\r
7033       }\r
7034       /* fall thru */\r
7035     case '\033': /* Escape key */\r
7036       SetWindowText(hwnd, "");\r
7037       cf.cbSize = sizeof(CHARFORMAT);\r
7038       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7039       if (consoleEcho) {\r
7040         cf.crTextColor = textAttribs[ColorNormal].color;\r
7041       } else {\r
7042         cf.crTextColor = COLOR_ECHOOFF;\r
7043       }\r
7044       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7045       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7046       return 0;\r
7047     case '\t':   /* Tab key */\r
7048       if (GetKeyState(VK_SHIFT) < 0) {\r
7049         /* shifted */\r
7050         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7051       } else {\r
7052         /* unshifted */\r
7053         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7054         if (buttonDesc[0].hwnd) {\r
7055           SetFocus(buttonDesc[0].hwnd);\r
7056         } else {\r
7057           SetFocus(hwndMain);\r
7058         }\r
7059       }\r
7060       return 0;\r
7061     case '\023': /* Ctrl+S */\r
7062       sendNextChar = TRUE;\r
7063       return 0;\r
7064     case '\021': /* Ctrl+Q */\r
7065       quoteNextChar = TRUE;\r
7066       return 0;\r
7067     JAWS_REPLAY\r
7068     default:\r
7069       break;\r
7070     }\r
7071     break;\r
7072   case WM_KEYDOWN:\r
7073     switch (wParam) {\r
7074     case VK_UP:\r
7075       GetWindowText(hwnd, buf, MSG_SIZ);\r
7076       p = PrevInHistory(buf);\r
7077       if (p != NULL) {\r
7078         SetWindowText(hwnd, p);\r
7079         sel.cpMin = 999999;\r
7080         sel.cpMax = 999999;\r
7081         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7082         return 0;\r
7083       }\r
7084       break;\r
7085     case VK_DOWN:\r
7086       p = NextInHistory();\r
7087       if (p != NULL) {\r
7088         SetWindowText(hwnd, p);\r
7089         sel.cpMin = 999999;\r
7090         sel.cpMax = 999999;\r
7091         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7092         return 0;\r
7093       }\r
7094       break;\r
7095     case VK_HOME:\r
7096     case VK_END:\r
7097       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7098       /* fall thru */\r
7099     case VK_PRIOR:\r
7100     case VK_NEXT:\r
7101       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7102       return 0;\r
7103     }\r
7104     break;\r
7105   case WM_MBUTTONDOWN:\r
7106     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7107       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7108     break;\r
7109   case WM_RBUTTONUP:\r
7110     if (GetKeyState(VK_SHIFT) & ~1) {\r
7111       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7112         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7113     } else {\r
7114       POINT pt;\r
7115       HMENU hmenu;\r
7116       hmenu = LoadMenu(hInst, "InputMenu");\r
7117       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7118       if (sel.cpMin == sel.cpMax) {\r
7119         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7120         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7121       }\r
7122       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7123         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7124       }\r
7125       pt.x = LOWORD(lParam);\r
7126       pt.y = HIWORD(lParam);\r
7127       MenuPopup(hwnd, pt, hmenu, -1);\r
7128     }\r
7129     return 0;\r
7130   case WM_COMMAND:\r
7131     switch (LOWORD(wParam)) { \r
7132     case IDM_Undo:\r
7133       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7134       return 0;\r
7135     case IDM_SelectAll:\r
7136       sel.cpMin = 0;\r
7137       sel.cpMax = -1; /*999999?*/\r
7138       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7139       return 0;\r
7140     case IDM_Cut:\r
7141       SendMessage(hwnd, WM_CUT, 0, 0);\r
7142       return 0;\r
7143     case IDM_Paste:\r
7144       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7145       return 0;\r
7146     case IDM_Copy:\r
7147       SendMessage(hwnd, WM_COPY, 0, 0);\r
7148       return 0;\r
7149     }\r
7150     break;\r
7151   }\r
7152   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7153 }\r
7154 \r
7155 #define CO_MAX  100000\r
7156 #define CO_TRIM   1000\r
7157 \r
7158 LRESULT CALLBACK\r
7159 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7160 {\r
7161   static SnapData sd;\r
7162   HWND hText, hInput;\r
7163   RECT rect;\r
7164   static int sizeX, sizeY;\r
7165   int newSizeX, newSizeY;\r
7166   MINMAXINFO *mmi;\r
7167   WORD wMask;\r
7168 \r
7169   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7170   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7171 \r
7172   switch (message) {\r
7173   case WM_NOTIFY:\r
7174     if (((NMHDR*)lParam)->code == EN_LINK)\r
7175     {\r
7176       ENLINK *pLink = (ENLINK*)lParam;\r
7177       if (pLink->msg == WM_LBUTTONUP)\r
7178       {\r
7179         TEXTRANGE tr;\r
7180 \r
7181         tr.chrg = pLink->chrg;\r
7182         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7183         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7184         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7185         free(tr.lpstrText);\r
7186       }\r
7187     }\r
7188     break;\r
7189   case WM_INITDIALOG: /* message: initialize dialog box */\r
7190     hwndConsole = hDlg;\r
7191     SetFocus(hInput);\r
7192     consoleTextWindowProc = (WNDPROC)\r
7193       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7194     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7195     consoleInputWindowProc = (WNDPROC)\r
7196       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7197     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7198     Colorize(ColorNormal, TRUE);\r
7199     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7200     ChangedConsoleFont();\r
7201     GetClientRect(hDlg, &rect);\r
7202     sizeX = rect.right;\r
7203     sizeY = rect.bottom;\r
7204     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7205         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7206       WINDOWPLACEMENT wp;\r
7207       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7208       wp.length = sizeof(WINDOWPLACEMENT);\r
7209       wp.flags = 0;\r
7210       wp.showCmd = SW_SHOW;\r
7211       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7212       wp.rcNormalPosition.left = wpConsole.x;\r
7213       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7214       wp.rcNormalPosition.top = wpConsole.y;\r
7215       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7216       SetWindowPlacement(hDlg, &wp);\r
7217     }\r
7218 \r
7219    // [HGM] Chessknight's change 2004-07-13\r
7220    else { /* Determine Defaults */\r
7221        WINDOWPLACEMENT wp;\r
7222        wpConsole.x = wpMain.width + 1;\r
7223        wpConsole.y = wpMain.y;\r
7224        wpConsole.width = screenWidth -  wpMain.width;\r
7225        wpConsole.height = wpMain.height;\r
7226        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7227        wp.length = sizeof(WINDOWPLACEMENT);\r
7228        wp.flags = 0;\r
7229        wp.showCmd = SW_SHOW;\r
7230        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7231        wp.rcNormalPosition.left = wpConsole.x;\r
7232        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7233        wp.rcNormalPosition.top = wpConsole.y;\r
7234        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7235        SetWindowPlacement(hDlg, &wp);\r
7236     }\r
7237 \r
7238    // Allow hText to highlight URLs and send notifications on them\r
7239    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7240    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7241    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7242    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7243 \r
7244     return FALSE;\r
7245 \r
7246   case WM_SETFOCUS:\r
7247     SetFocus(hInput);\r
7248     return 0;\r
7249 \r
7250   case WM_CLOSE:\r
7251     ExitEvent(0);\r
7252     /* not reached */\r
7253     break;\r
7254 \r
7255   case WM_SIZE:\r
7256     if (IsIconic(hDlg)) break;\r
7257     newSizeX = LOWORD(lParam);\r
7258     newSizeY = HIWORD(lParam);\r
7259     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7260       RECT rectText, rectInput;\r
7261       POINT pt;\r
7262       int newTextHeight, newTextWidth;\r
7263       GetWindowRect(hText, &rectText);\r
7264       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7265       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7266       if (newTextHeight < 0) {\r
7267         newSizeY += -newTextHeight;\r
7268         newTextHeight = 0;\r
7269       }\r
7270       SetWindowPos(hText, NULL, 0, 0,\r
7271         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7272       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7273       pt.x = rectInput.left;\r
7274       pt.y = rectInput.top + newSizeY - sizeY;\r
7275       ScreenToClient(hDlg, &pt);\r
7276       SetWindowPos(hInput, NULL, \r
7277         pt.x, pt.y, /* needs client coords */   \r
7278         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7279         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7280     }\r
7281     sizeX = newSizeX;\r
7282     sizeY = newSizeY;\r
7283     break;\r
7284 \r
7285   case WM_GETMINMAXINFO:\r
7286     /* Prevent resizing window too small */\r
7287     mmi = (MINMAXINFO *) lParam;\r
7288     mmi->ptMinTrackSize.x = 100;\r
7289     mmi->ptMinTrackSize.y = 100;\r
7290     break;\r
7291 \r
7292   /* [AS] Snapping */\r
7293   case WM_ENTERSIZEMOVE:\r
7294     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7295 \r
7296   case WM_SIZING:\r
7297     return OnSizing( &sd, hDlg, wParam, lParam );\r
7298 \r
7299   case WM_MOVING:\r
7300     return OnMoving( &sd, hDlg, wParam, lParam );\r
7301 \r
7302   case WM_EXITSIZEMOVE:\r
7303         UpdateICSWidth(hText);\r
7304     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7305   }\r
7306 \r
7307   return DefWindowProc(hDlg, message, wParam, lParam);\r
7308 }\r
7309 \r
7310 \r
7311 VOID\r
7312 ConsoleCreate()\r
7313 {\r
7314   HWND hCons;\r
7315   if (hwndConsole) return;\r
7316   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7317   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7318 }\r
7319 \r
7320 \r
7321 VOID\r
7322 ConsoleOutput(char* data, int length, int forceVisible)\r
7323 {\r
7324   HWND hText;\r
7325   int trim, exlen;\r
7326   char *p, *q;\r
7327   char buf[CO_MAX+1];\r
7328   POINT pEnd;\r
7329   RECT rect;\r
7330   static int delayLF = 0;\r
7331   CHARRANGE savesel, sel;\r
7332 \r
7333   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7334   p = data;\r
7335   q = buf;\r
7336   if (delayLF) {\r
7337     *q++ = '\r';\r
7338     *q++ = '\n';\r
7339     delayLF = 0;\r
7340   }\r
7341   while (length--) {\r
7342     if (*p == '\n') {\r
7343       if (*++p) {\r
7344         *q++ = '\r';\r
7345         *q++ = '\n';\r
7346       } else {\r
7347         delayLF = 1;\r
7348       }\r
7349     } else if (*p == '\007') {\r
7350        MyPlaySound(&sounds[(int)SoundBell]);\r
7351        p++;\r
7352     } else {\r
7353       *q++ = *p++;\r
7354     }\r
7355   }\r
7356   *q = NULLCHAR;\r
7357   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7358   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7359   /* Save current selection */\r
7360   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7361   exlen = GetWindowTextLength(hText);\r
7362   /* Find out whether current end of text is visible */\r
7363   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7364   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7365   /* Trim existing text if it's too long */\r
7366   if (exlen + (q - buf) > CO_MAX) {\r
7367     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7368     sel.cpMin = 0;\r
7369     sel.cpMax = trim;\r
7370     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7371     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7372     exlen -= trim;\r
7373     savesel.cpMin -= trim;\r
7374     savesel.cpMax -= trim;\r
7375     if (exlen < 0) exlen = 0;\r
7376     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7377     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7378   }\r
7379   /* Append the new text */\r
7380   sel.cpMin = exlen;\r
7381   sel.cpMax = exlen;\r
7382   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7383   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7384   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7385   if (forceVisible || exlen == 0 ||\r
7386       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7387        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7388     /* Scroll to make new end of text visible if old end of text\r
7389        was visible or new text is an echo of user typein */\r
7390     sel.cpMin = 9999999;\r
7391     sel.cpMax = 9999999;\r
7392     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7393     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7394     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7395     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7396   }\r
7397   if (savesel.cpMax == exlen || forceVisible) {\r
7398     /* Move insert point to new end of text if it was at the old\r
7399        end of text or if the new text is an echo of user typein */\r
7400     sel.cpMin = 9999999;\r
7401     sel.cpMax = 9999999;\r
7402     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7403   } else {\r
7404     /* Restore previous selection */\r
7405     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7406   }\r
7407   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7408 }\r
7409 \r
7410 /*---------*/\r
7411 \r
7412 \r
7413 void\r
7414 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7415 {\r
7416   char buf[100];\r
7417   char *str;\r
7418   COLORREF oldFg, oldBg;\r
7419   HFONT oldFont;\r
7420   RECT rect;\r
7421 \r
7422   if(copyNumber > 1)
7423     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7424 \r
7425   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7426   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7427   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7428 \r
7429   rect.left = x;\r
7430   rect.right = x + squareSize;\r
7431   rect.top  = y;\r
7432   rect.bottom = y + squareSize;\r
7433   str = buf;\r
7434 \r
7435   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7436                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7437              y, ETO_CLIPPED|ETO_OPAQUE,\r
7438              &rect, str, strlen(str), NULL);\r
7439 \r
7440   (void) SetTextColor(hdc, oldFg);\r
7441   (void) SetBkColor(hdc, oldBg);\r
7442   (void) SelectObject(hdc, oldFont);\r
7443 }\r
7444 \r
7445 void\r
7446 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7447               RECT *rect, char *color, char *flagFell)\r
7448 {\r
7449   char buf[100];\r
7450   char *str;\r
7451   COLORREF oldFg, oldBg;\r
7452   HFONT oldFont;\r
7453 \r
7454   if (appData.clockMode) {\r
7455     if (tinyLayout)\r
7456       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7457     else\r
7458       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7459     str = buf;\r
7460   } else {\r
7461     str = color;\r
7462   }\r
7463 \r
7464   if (highlight) {\r
7465     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7466     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7467   } else {\r
7468     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7469     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7470   }\r
7471   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7472 \r
7473   JAWS_SILENCE\r
7474 \r
7475   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7476              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7477              rect, str, strlen(str), NULL);\r
7478   if(logoHeight > 0 && appData.clockMode) {\r
7479       RECT r;\r
7480       str += strlen(color)+2;\r
7481       r.top = rect->top + logoHeight/2;\r
7482       r.left = rect->left;\r
7483       r.right = rect->right;\r
7484       r.bottom = rect->bottom;\r
7485       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7486                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7487                  &r, str, strlen(str), NULL);\r
7488   }\r
7489   (void) SetTextColor(hdc, oldFg);\r
7490   (void) SetBkColor(hdc, oldBg);\r
7491   (void) SelectObject(hdc, oldFont);\r
7492 }\r
7493 \r
7494 \r
7495 int\r
7496 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7497            OVERLAPPED *ovl)\r
7498 {\r
7499   int ok, err;\r
7500 \r
7501   /* [AS]  */\r
7502   if( count <= 0 ) {\r
7503     if (appData.debugMode) {\r
7504       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7505     }\r
7506 \r
7507     return ERROR_INVALID_USER_BUFFER;\r
7508   }\r
7509 \r
7510   ResetEvent(ovl->hEvent);\r
7511   ovl->Offset = ovl->OffsetHigh = 0;\r
7512   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7513   if (ok) {\r
7514     err = NO_ERROR;\r
7515   } else {\r
7516     err = GetLastError();\r
7517     if (err == ERROR_IO_PENDING) {\r
7518       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7519       if (ok)\r
7520         err = NO_ERROR;\r
7521       else\r
7522         err = GetLastError();\r
7523     }\r
7524   }\r
7525   return err;\r
7526 }\r
7527 \r
7528 int\r
7529 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7530             OVERLAPPED *ovl)\r
7531 {\r
7532   int ok, err;\r
7533 \r
7534   ResetEvent(ovl->hEvent);\r
7535   ovl->Offset = ovl->OffsetHigh = 0;\r
7536   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7537   if (ok) {\r
7538     err = NO_ERROR;\r
7539   } else {\r
7540     err = GetLastError();\r
7541     if (err == ERROR_IO_PENDING) {\r
7542       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7543       if (ok)\r
7544         err = NO_ERROR;\r
7545       else\r
7546         err = GetLastError();\r
7547     }\r
7548   }\r
7549   return err;\r
7550 }\r
7551 \r
7552 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7553 void CheckForInputBufferFull( InputSource * is )\r
7554 {\r
7555     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7556         /* Look for end of line */\r
7557         char * p = is->buf;\r
7558         \r
7559         while( p < is->next && *p != '\n' ) {\r
7560             p++;\r
7561         }\r
7562 \r
7563         if( p >= is->next ) {\r
7564             if (appData.debugMode) {\r
7565                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7566             }\r
7567 \r
7568             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7569             is->count = (DWORD) -1;\r
7570             is->next = is->buf;\r
7571         }\r
7572     }\r
7573 }\r
7574 \r
7575 DWORD\r
7576 InputThread(LPVOID arg)\r
7577 {\r
7578   InputSource *is;\r
7579   OVERLAPPED ovl;\r
7580 \r
7581   is = (InputSource *) arg;\r
7582   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7583   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7584   while (is->hThread != NULL) {\r
7585     is->error = DoReadFile(is->hFile, is->next,\r
7586                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7587                            &is->count, &ovl);\r
7588     if (is->error == NO_ERROR) {\r
7589       is->next += is->count;\r
7590     } else {\r
7591       if (is->error == ERROR_BROKEN_PIPE) {\r
7592         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7593         is->count = 0;\r
7594       } else {\r
7595         is->count = (DWORD) -1;\r
7596         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7597         break; \r
7598       }\r
7599     }\r
7600 \r
7601     CheckForInputBufferFull( is );\r
7602 \r
7603     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7604 \r
7605     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7606 \r
7607     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7608   }\r
7609 \r
7610   CloseHandle(ovl.hEvent);\r
7611   CloseHandle(is->hFile);\r
7612 \r
7613   if (appData.debugMode) {\r
7614     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7615   }\r
7616 \r
7617   return 0;\r
7618 }\r
7619 \r
7620 \r
7621 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7622 DWORD\r
7623 NonOvlInputThread(LPVOID arg)\r
7624 {\r
7625   InputSource *is;\r
7626   char *p, *q;\r
7627   int i;\r
7628   char prev;\r
7629 \r
7630   is = (InputSource *) arg;\r
7631   while (is->hThread != NULL) {\r
7632     is->error = ReadFile(is->hFile, is->next,\r
7633                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7634                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7635     if (is->error == NO_ERROR) {\r
7636       /* Change CRLF to LF */\r
7637       if (is->next > is->buf) {\r
7638         p = is->next - 1;\r
7639         i = is->count + 1;\r
7640       } else {\r
7641         p = is->next;\r
7642         i = is->count;\r
7643       }\r
7644       q = p;\r
7645       prev = NULLCHAR;\r
7646       while (i > 0) {\r
7647         if (prev == '\r' && *p == '\n') {\r
7648           *(q-1) = '\n';\r
7649           is->count--;\r
7650         } else { \r
7651           *q++ = *p;\r
7652         }\r
7653         prev = *p++;\r
7654         i--;\r
7655       }\r
7656       *q = NULLCHAR;\r
7657       is->next = q;\r
7658     } else {\r
7659       if (is->error == ERROR_BROKEN_PIPE) {\r
7660         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7661         is->count = 0; \r
7662       } else {\r
7663         is->count = (DWORD) -1;\r
7664       }\r
7665     }\r
7666 \r
7667     CheckForInputBufferFull( is );\r
7668 \r
7669     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7670 \r
7671     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7672 \r
7673     if (is->count < 0) break;  /* Quit on error */\r
7674   }\r
7675   CloseHandle(is->hFile);\r
7676   return 0;\r
7677 }\r
7678 \r
7679 DWORD\r
7680 SocketInputThread(LPVOID arg)\r
7681 {\r
7682   InputSource *is;\r
7683 \r
7684   is = (InputSource *) arg;\r
7685   while (is->hThread != NULL) {\r
7686     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7687     if ((int)is->count == SOCKET_ERROR) {\r
7688       is->count = (DWORD) -1;\r
7689       is->error = WSAGetLastError();\r
7690     } else {\r
7691       is->error = NO_ERROR;\r
7692       is->next += is->count;\r
7693       if (is->count == 0 && is->second == is) {\r
7694         /* End of file on stderr; quit with no message */\r
7695         break;\r
7696       }\r
7697     }\r
7698     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7699 \r
7700     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7701 \r
7702     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7703   }\r
7704   return 0;\r
7705 }\r
7706 \r
7707 VOID\r
7708 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7709 {\r
7710   InputSource *is;\r
7711 \r
7712   is = (InputSource *) lParam;\r
7713   if (is->lineByLine) {\r
7714     /* Feed in lines one by one */\r
7715     char *p = is->buf;\r
7716     char *q = p;\r
7717     while (q < is->next) {\r
7718       if (*q++ == '\n') {\r
7719         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7720         p = q;\r
7721       }\r
7722     }\r
7723     \r
7724     /* Move any partial line to the start of the buffer */\r
7725     q = is->buf;\r
7726     while (p < is->next) {\r
7727       *q++ = *p++;\r
7728     }\r
7729     is->next = q;\r
7730 \r
7731     if (is->error != NO_ERROR || is->count == 0) {\r
7732       /* Notify backend of the error.  Note: If there was a partial\r
7733          line at the end, it is not flushed through. */\r
7734       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7735     }\r
7736   } else {\r
7737     /* Feed in the whole chunk of input at once */\r
7738     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7739     is->next = is->buf;\r
7740   }\r
7741 }\r
7742 \r
7743 /*---------------------------------------------------------------------------*\\r
7744  *\r
7745  *  Menu enables. Used when setting various modes.\r
7746  *\r
7747 \*---------------------------------------------------------------------------*/\r
7748 \r
7749 typedef struct {\r
7750   int item;\r
7751   int flags;\r
7752 } Enables;\r
7753 \r
7754 VOID\r
7755 GreyRevert(Boolean grey)\r
7756 { // [HGM] vari: for retracting variations in local mode\r
7757   HMENU hmenu = GetMenu(hwndMain);\r
7758   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7759   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7760 }\r
7761 \r
7762 VOID\r
7763 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7764 {\r
7765   while (enab->item > 0) {\r
7766     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7767     enab++;\r
7768   }\r
7769 }\r
7770 \r
7771 Enables gnuEnables[] = {\r
7772   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7773   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7774   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7775   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7776   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7777   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7778   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7779   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7780   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7781   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7782   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7783   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7784   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7785   { -1, -1 }\r
7786 };\r
7787 \r
7788 Enables icsEnables[] = {\r
7789   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7790   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7791   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7792   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7793   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7794   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7795   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7796   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7797   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7798   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7799   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7800   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7801   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7802   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7803   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7804   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7805   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7806   { -1, -1 }\r
7807 };\r
7808 \r
7809 #if ZIPPY\r
7810 Enables zippyEnables[] = {\r
7811   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7812   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7813   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7814   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7815   { -1, -1 }\r
7816 };\r
7817 #endif\r
7818 \r
7819 Enables ncpEnables[] = {\r
7820   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7821   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7822   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7823   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7824   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7825   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7826   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7827   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7828   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7829   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7830   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7831   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7832   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7833   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7834   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7835   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7836   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7837   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7838   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7839   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7840   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7841   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7842   { -1, -1 }\r
7843 };\r
7844 \r
7845 Enables trainingOnEnables[] = {\r
7846   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7847   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7848   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7849   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7850   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7851   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7852   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7853   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7854   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7855   { -1, -1 }\r
7856 };\r
7857 \r
7858 Enables trainingOffEnables[] = {\r
7859   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7860   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7861   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7862   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7863   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7864   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7865   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7866   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7867   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7868   { -1, -1 }\r
7869 };\r
7870 \r
7871 /* These modify either ncpEnables or gnuEnables */\r
7872 Enables cmailEnables[] = {\r
7873   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7874   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7875   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7876   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7877   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7878   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7879   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7880   { -1, -1 }\r
7881 };\r
7882 \r
7883 Enables machineThinkingEnables[] = {\r
7884   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7885   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7886   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7887   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7888   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7889   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7890   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7891   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7892   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7893   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7894   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7895   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7896   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7897   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7898   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7899   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7900   { -1, -1 }\r
7901 };\r
7902 \r
7903 Enables userThinkingEnables[] = {\r
7904   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7905   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7906   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7907   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7908   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7909   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7910   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7911   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7912   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7913   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7914   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7915   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7916   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7917   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7918   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7919   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7920   { -1, -1 }\r
7921 };\r
7922 \r
7923 /*---------------------------------------------------------------------------*\\r
7924  *\r
7925  *  Front-end interface functions exported by XBoard.\r
7926  *  Functions appear in same order as prototypes in frontend.h.\r
7927  * \r
7928 \*---------------------------------------------------------------------------*/\r
7929 VOID\r
7930 ModeHighlight()\r
7931 {\r
7932   static UINT prevChecked = 0;\r
7933   static int prevPausing = 0;\r
7934   UINT nowChecked;\r
7935 \r
7936   if (pausing != prevPausing) {\r
7937     prevPausing = pausing;\r
7938     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7939                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7940     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7941   }\r
7942 \r
7943   switch (gameMode) {\r
7944   case BeginningOfGame:\r
7945     if (appData.icsActive)\r
7946       nowChecked = IDM_IcsClient;\r
7947     else if (appData.noChessProgram)\r
7948       nowChecked = IDM_EditGame;\r
7949     else\r
7950       nowChecked = IDM_MachineBlack;\r
7951     break;\r
7952   case MachinePlaysBlack:\r
7953     nowChecked = IDM_MachineBlack;\r
7954     break;\r
7955   case MachinePlaysWhite:\r
7956     nowChecked = IDM_MachineWhite;\r
7957     break;\r
7958   case TwoMachinesPlay:\r
7959     nowChecked = matchMode ? IDM_Match : IDM_TwoMachines; // [HGM] match\r
7960     break;\r
7961   case AnalyzeMode:\r
7962     nowChecked = IDM_AnalysisMode;\r
7963     break;\r
7964   case AnalyzeFile:\r
7965     nowChecked = IDM_AnalyzeFile;\r
7966     break;\r
7967   case EditGame:\r
7968     nowChecked = IDM_EditGame;\r
7969     break;\r
7970   case PlayFromGameFile:\r
7971     nowChecked = IDM_LoadGame;\r
7972     break;\r
7973   case EditPosition:\r
7974     nowChecked = IDM_EditPosition;\r
7975     break;\r
7976   case Training:\r
7977     nowChecked = IDM_Training;\r
7978     break;\r
7979   case IcsPlayingWhite:\r
7980   case IcsPlayingBlack:\r
7981   case IcsObserving:\r
7982   case IcsIdle:\r
7983     nowChecked = IDM_IcsClient;\r
7984     break;\r
7985   default:\r
7986   case EndOfGame:\r
7987     nowChecked = 0;\r
7988     break;\r
7989   }\r
7990   if (prevChecked != 0)\r
7991     (void) CheckMenuItem(GetMenu(hwndMain),\r
7992                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
7993   if (nowChecked != 0)\r
7994     (void) CheckMenuItem(GetMenu(hwndMain),\r
7995                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
7996 \r
7997   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
7998     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
7999                           MF_BYCOMMAND|MF_ENABLED);\r
8000   } else {\r
8001     (void) EnableMenuItem(GetMenu(hwndMain), \r
8002                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8003   }\r
8004 \r
8005   prevChecked = nowChecked;\r
8006 \r
8007   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8008   if (appData.icsActive) {\r
8009        if (appData.icsEngineAnalyze) {\r
8010                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8011                        MF_BYCOMMAND|MF_CHECKED);\r
8012        } else {\r
8013                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8014                        MF_BYCOMMAND|MF_UNCHECKED);\r
8015        }\r
8016   }\r
8017   DisplayLogos(); // [HGM] logos: mode change could have altered logos\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), IDM_IcsOptions,\r
8026     MF_BYCOMMAND|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     DrawMenuBar(hwndMain);\r
8049 }\r
8050 \r
8051 VOID\r
8052 SetCmailMode()\r
8053 {\r
8054   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8055 }\r
8056 \r
8057 VOID \r
8058 SetTrainingModeOn()\r
8059 {\r
8060   int i;\r
8061   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8062   for (i = 0; i < N_BUTTONS; i++) {\r
8063     if (buttonDesc[i].hwnd != NULL)\r
8064       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8065   }\r
8066   CommentPopDown();\r
8067 }\r
8068 \r
8069 VOID SetTrainingModeOff()\r
8070 {\r
8071   int i;\r
8072   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8073   for (i = 0; i < N_BUTTONS; i++) {\r
8074     if (buttonDesc[i].hwnd != NULL)\r
8075       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8076   }\r
8077 }\r
8078 \r
8079 \r
8080 VOID\r
8081 SetUserThinkingEnables()\r
8082 {\r
8083   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8084 }\r
8085 \r
8086 VOID\r
8087 SetMachineThinkingEnables()\r
8088 {\r
8089   HMENU hMenu = GetMenu(hwndMain);\r
8090   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8091 \r
8092   SetMenuEnables(hMenu, machineThinkingEnables);\r
8093 \r
8094   if (gameMode == MachinePlaysBlack) {\r
8095     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8096   } else if (gameMode == MachinePlaysWhite) {\r
8097     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8098   } else if (gameMode == TwoMachinesPlay) {\r
8099     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8100   }\r
8101 }\r
8102 \r
8103 \r
8104 VOID\r
8105 DisplayTitle(char *str)\r
8106 {\r
8107   char title[MSG_SIZ], *host;\r
8108   if (str[0] != NULLCHAR) {\r
8109     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8110   } else if (appData.icsActive) {\r
8111     if (appData.icsCommPort[0] != NULLCHAR)\r
8112       host = "ICS";\r
8113     else \r
8114       host = appData.icsHost;\r
8115       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8116   } else if (appData.noChessProgram) {\r
8117     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8118   } else {\r
8119     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8120     strcat(title, ": ");\r
8121     strcat(title, first.tidy);\r
8122   }\r
8123   SetWindowText(hwndMain, title);\r
8124 }\r
8125 \r
8126 \r
8127 VOID\r
8128 DisplayMessage(char *str1, char *str2)\r
8129 {\r
8130   HDC hdc;\r
8131   HFONT oldFont;\r
8132   int remain = MESSAGE_TEXT_MAX - 1;\r
8133   int len;\r
8134 \r
8135   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8136   messageText[0] = NULLCHAR;\r
8137   if (*str1) {\r
8138     len = strlen(str1);\r
8139     if (len > remain) len = remain;\r
8140     strncpy(messageText, str1, len);\r
8141     messageText[len] = NULLCHAR;\r
8142     remain -= len;\r
8143   }\r
8144   if (*str2 && remain >= 2) {\r
8145     if (*str1) {\r
8146       strcat(messageText, "  ");\r
8147       remain -= 2;\r
8148     }\r
8149     len = strlen(str2);\r
8150     if (len > remain) len = remain;\r
8151     strncat(messageText, str2, len);\r
8152   }\r
8153   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8154 \r
8155   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8156 \r
8157   SAYMACHINEMOVE();\r
8158 \r
8159   hdc = GetDC(hwndMain);\r
8160   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8161   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8162              &messageRect, messageText, strlen(messageText), NULL);\r
8163   (void) SelectObject(hdc, oldFont);\r
8164   (void) ReleaseDC(hwndMain, hdc);\r
8165 }\r
8166 \r
8167 VOID\r
8168 DisplayError(char *str, int error)\r
8169 {\r
8170   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8171   int len;\r
8172 \r
8173   if (error == 0) {\r
8174     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8175   } else {\r
8176     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8177                         NULL, error, LANG_NEUTRAL,\r
8178                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8179     if (len > 0) {\r
8180       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8181     } else {\r
8182       ErrorMap *em = errmap;\r
8183       while (em->err != 0 && em->err != error) em++;\r
8184       if (em->err != 0) {\r
8185         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8186       } else {\r
8187         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8188       }\r
8189     }\r
8190   }\r
8191   \r
8192   ErrorPopUp(_("Error"), buf);\r
8193 }\r
8194 \r
8195 \r
8196 VOID\r
8197 DisplayMoveError(char *str)\r
8198 {\r
8199   fromX = fromY = -1;\r
8200   ClearHighlights();\r
8201   DrawPosition(FALSE, NULL);\r
8202   if (appData.popupMoveErrors) {\r
8203     ErrorPopUp(_("Error"), str);\r
8204   } else {\r
8205     DisplayMessage(str, "");\r
8206     moveErrorMessageUp = TRUE;\r
8207   }\r
8208 }\r
8209 \r
8210 VOID\r
8211 DisplayFatalError(char *str, int error, int exitStatus)\r
8212 {\r
8213   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8214   int len;\r
8215   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8216 \r
8217   if (error != 0) {\r
8218     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8219                         NULL, error, LANG_NEUTRAL,\r
8220                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8221     if (len > 0) {\r
8222       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8223     } else {\r
8224       ErrorMap *em = errmap;\r
8225       while (em->err != 0 && em->err != error) em++;\r
8226       if (em->err != 0) {\r
8227         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8228       } else {\r
8229         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8230       }\r
8231     }\r
8232     str = buf;\r
8233   }\r
8234   if (appData.debugMode) {\r
8235     fprintf(debugFP, "%s: %s\n", label, str);\r
8236   }\r
8237   if (appData.popupExitMessage) {\r
8238     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8239                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8240   }\r
8241   ExitEvent(exitStatus);\r
8242 }\r
8243 \r
8244 \r
8245 VOID\r
8246 DisplayInformation(char *str)\r
8247 {\r
8248   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8249 }\r
8250 \r
8251 \r
8252 VOID\r
8253 DisplayNote(char *str)\r
8254 {\r
8255   ErrorPopUp(_("Note"), str);\r
8256 }\r
8257 \r
8258 \r
8259 typedef struct {\r
8260   char *title, *question, *replyPrefix;\r
8261   ProcRef pr;\r
8262 } QuestionParams;\r
8263 \r
8264 LRESULT CALLBACK\r
8265 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8266 {\r
8267   static QuestionParams *qp;\r
8268   char reply[MSG_SIZ];\r
8269   int len, err;\r
8270 \r
8271   switch (message) {\r
8272   case WM_INITDIALOG:\r
8273     qp = (QuestionParams *) lParam;\r
8274     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8275     Translate(hDlg, DLG_Question);\r
8276     SetWindowText(hDlg, qp->title);\r
8277     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8278     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8279     return FALSE;\r
8280 \r
8281   case WM_COMMAND:\r
8282     switch (LOWORD(wParam)) {\r
8283     case IDOK:\r
8284       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8285       if (*reply) strcat(reply, " ");\r
8286       len = strlen(reply);\r
8287       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8288       strcat(reply, "\n");\r
8289       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8290       EndDialog(hDlg, TRUE);\r
8291       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8292       return TRUE;\r
8293     case IDCANCEL:\r
8294       EndDialog(hDlg, FALSE);\r
8295       return TRUE;\r
8296     default:\r
8297       break;\r
8298     }\r
8299     break;\r
8300   }\r
8301   return FALSE;\r
8302 }\r
8303 \r
8304 VOID\r
8305 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8306 {\r
8307     QuestionParams qp;\r
8308     FARPROC lpProc;\r
8309     \r
8310     qp.title = title;\r
8311     qp.question = question;\r
8312     qp.replyPrefix = replyPrefix;\r
8313     qp.pr = pr;\r
8314     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8315     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8316       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8317     FreeProcInstance(lpProc);\r
8318 }\r
8319 \r
8320 /* [AS] Pick FRC position */\r
8321 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8322 {\r
8323     static int * lpIndexFRC;\r
8324     BOOL index_is_ok;\r
8325     char buf[16];\r
8326 \r
8327     switch( message )\r
8328     {\r
8329     case WM_INITDIALOG:\r
8330         lpIndexFRC = (int *) lParam;\r
8331 \r
8332         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8333         Translate(hDlg, DLG_NewGameFRC);\r
8334 \r
8335         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8336         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8337         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8338         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8339 \r
8340         break;\r
8341 \r
8342     case WM_COMMAND:\r
8343         switch( LOWORD(wParam) ) {\r
8344         case IDOK:\r
8345             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8346             EndDialog( hDlg, 0 );\r
8347             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8348             return TRUE;\r
8349         case IDCANCEL:\r
8350             EndDialog( hDlg, 1 );   \r
8351             return TRUE;\r
8352         case IDC_NFG_Edit:\r
8353             if( HIWORD(wParam) == EN_CHANGE ) {\r
8354                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8355 \r
8356                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8357             }\r
8358             return TRUE;\r
8359         case IDC_NFG_Random:\r
8360           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8361             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8362             return TRUE;\r
8363         }\r
8364 \r
8365         break;\r
8366     }\r
8367 \r
8368     return FALSE;\r
8369 }\r
8370 \r
8371 int NewGameFRC()\r
8372 {\r
8373     int result;\r
8374     int index = appData.defaultFrcPosition;\r
8375     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8376 \r
8377     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8378 \r
8379     if( result == 0 ) {\r
8380         appData.defaultFrcPosition = index;\r
8381     }\r
8382 \r
8383     return result;\r
8384 }\r
8385 \r
8386 /* [AS] Game list options. Refactored by HGM */\r
8387 \r
8388 HWND gameListOptionsDialog;\r
8389 \r
8390 // low-level front-end: clear text edit / list widget\r
8391 void\r
8392 GLT_ClearList()\r
8393 {\r
8394     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8395 }\r
8396 \r
8397 // low-level front-end: clear text edit / list widget\r
8398 void\r
8399 GLT_DeSelectList()\r
8400 {\r
8401     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8402 }\r
8403 \r
8404 // low-level front-end: append line to text edit / list widget\r
8405 void\r
8406 GLT_AddToList( char *name )\r
8407 {\r
8408     if( name != 0 ) {\r
8409             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8410     }\r
8411 }\r
8412 \r
8413 // low-level front-end: get line from text edit / list widget\r
8414 Boolean\r
8415 GLT_GetFromList( int index, char *name )\r
8416 {\r
8417     if( name != 0 ) {\r
8418             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8419                 return TRUE;\r
8420     }\r
8421     return FALSE;\r
8422 }\r
8423 \r
8424 void GLT_MoveSelection( HWND hDlg, int delta )\r
8425 {\r
8426     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8427     int idx2 = idx1 + delta;\r
8428     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8429 \r
8430     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8431         char buf[128];\r
8432 \r
8433         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8434         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8435         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8436         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8437     }\r
8438 }\r
8439 \r
8440 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8441 {\r
8442     switch( message )\r
8443     {\r
8444     case WM_INITDIALOG:\r
8445         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8446         \r
8447         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8448         Translate(hDlg, DLG_GameListOptions);\r
8449 \r
8450         /* Initialize list */\r
8451         GLT_TagsToList( lpUserGLT );\r
8452 \r
8453         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8454 \r
8455         break;\r
8456 \r
8457     case WM_COMMAND:\r
8458         switch( LOWORD(wParam) ) {\r
8459         case IDOK:\r
8460             GLT_ParseList();\r
8461             EndDialog( hDlg, 0 );\r
8462             return TRUE;\r
8463         case IDCANCEL:\r
8464             EndDialog( hDlg, 1 );\r
8465             return TRUE;\r
8466 \r
8467         case IDC_GLT_Default:\r
8468             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8469             return TRUE;\r
8470 \r
8471         case IDC_GLT_Restore:\r
8472             GLT_TagsToList( appData.gameListTags );\r
8473             return TRUE;\r
8474 \r
8475         case IDC_GLT_Up:\r
8476             GLT_MoveSelection( hDlg, -1 );\r
8477             return TRUE;\r
8478 \r
8479         case IDC_GLT_Down:\r
8480             GLT_MoveSelection( hDlg, +1 );\r
8481             return TRUE;\r
8482         }\r
8483 \r
8484         break;\r
8485     }\r
8486 \r
8487     return FALSE;\r
8488 }\r
8489 \r
8490 int GameListOptions()\r
8491 {\r
8492     int result;\r
8493     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8494 \r
8495       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8496 \r
8497     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8498 \r
8499     if( result == 0 ) {\r
8500         /* [AS] Memory leak here! */\r
8501         appData.gameListTags = strdup( lpUserGLT ); \r
8502     }\r
8503 \r
8504     return result;\r
8505 }\r
8506 \r
8507 VOID\r
8508 DisplayIcsInteractionTitle(char *str)\r
8509 {\r
8510   char consoleTitle[MSG_SIZ];\r
8511 \r
8512     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8513   SetWindowText(hwndConsole, consoleTitle);\r
8514 }\r
8515 \r
8516 void\r
8517 DrawPosition(int fullRedraw, Board board)\r
8518 {\r
8519   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8520 }\r
8521 \r
8522 void NotifyFrontendLogin()\r
8523 {\r
8524         if (hwndConsole)\r
8525                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8526 }\r
8527 \r
8528 VOID\r
8529 ResetFrontEnd()\r
8530 {\r
8531   fromX = fromY = -1;\r
8532   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8533     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8534     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8535     dragInfo.lastpos = dragInfo.pos;\r
8536     dragInfo.start.x = dragInfo.start.y = -1;\r
8537     dragInfo.from = dragInfo.start;\r
8538     ReleaseCapture();\r
8539     DrawPosition(TRUE, NULL);\r
8540   }\r
8541   TagsPopDown();\r
8542 }\r
8543 \r
8544 \r
8545 VOID\r
8546 CommentPopUp(char *title, char *str)\r
8547 {\r
8548   HWND hwnd = GetActiveWindow();\r
8549   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8550   SAY(str);\r
8551   SetActiveWindow(hwnd);\r
8552 }\r
8553 \r
8554 VOID\r
8555 CommentPopDown(void)\r
8556 {\r
8557   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8558   if (commentDialog) {\r
8559     ShowWindow(commentDialog, SW_HIDE);\r
8560   }\r
8561   commentUp = FALSE;\r
8562 }\r
8563 \r
8564 VOID\r
8565 EditCommentPopUp(int index, char *title, char *str)\r
8566 {\r
8567   EitherCommentPopUp(index, title, str, TRUE);\r
8568 }\r
8569 \r
8570 \r
8571 VOID\r
8572 RingBell()\r
8573 {\r
8574   MyPlaySound(&sounds[(int)SoundMove]);\r
8575 }\r
8576 \r
8577 VOID PlayIcsWinSound()\r
8578 {\r
8579   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8580 }\r
8581 \r
8582 VOID PlayIcsLossSound()\r
8583 {\r
8584   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8585 }\r
8586 \r
8587 VOID PlayIcsDrawSound()\r
8588 {\r
8589   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8590 }\r
8591 \r
8592 VOID PlayIcsUnfinishedSound()\r
8593 {\r
8594   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8595 }\r
8596 \r
8597 VOID\r
8598 PlayAlarmSound()\r
8599 {\r
8600   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8601 }\r
8602 \r
8603 \r
8604 VOID\r
8605 EchoOn()\r
8606 {\r
8607   HWND hInput;\r
8608   consoleEcho = TRUE;\r
8609   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8610   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8611   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8612 }\r
8613 \r
8614 \r
8615 VOID\r
8616 EchoOff()\r
8617 {\r
8618   CHARFORMAT cf;\r
8619   HWND hInput;\r
8620   consoleEcho = FALSE;\r
8621   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8622   /* This works OK: set text and background both to the same color */\r
8623   cf = consoleCF;\r
8624   cf.crTextColor = COLOR_ECHOOFF;\r
8625   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8626   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8627 }\r
8628 \r
8629 /* No Raw()...? */\r
8630 \r
8631 void Colorize(ColorClass cc, int continuation)\r
8632 {\r
8633   currentColorClass = cc;\r
8634   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8635   consoleCF.crTextColor = textAttribs[cc].color;\r
8636   consoleCF.dwEffects = textAttribs[cc].effects;\r
8637   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8638 }\r
8639 \r
8640 char *\r
8641 UserName()\r
8642 {\r
8643   static char buf[MSG_SIZ];\r
8644   DWORD bufsiz = MSG_SIZ;\r
8645 \r
8646   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8647         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8648   }\r
8649   if (!GetUserName(buf, &bufsiz)) {\r
8650     /*DisplayError("Error getting user name", GetLastError());*/\r
8651     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8652   }\r
8653   return buf;\r
8654 }\r
8655 \r
8656 char *\r
8657 HostName()\r
8658 {\r
8659   static char buf[MSG_SIZ];\r
8660   DWORD bufsiz = MSG_SIZ;\r
8661 \r
8662   if (!GetComputerName(buf, &bufsiz)) {\r
8663     /*DisplayError("Error getting host name", GetLastError());*/\r
8664     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8665   }\r
8666   return buf;\r
8667 }\r
8668 \r
8669 \r
8670 int\r
8671 ClockTimerRunning()\r
8672 {\r
8673   return clockTimerEvent != 0;\r
8674 }\r
8675 \r
8676 int\r
8677 StopClockTimer()\r
8678 {\r
8679   if (clockTimerEvent == 0) return FALSE;\r
8680   KillTimer(hwndMain, clockTimerEvent);\r
8681   clockTimerEvent = 0;\r
8682   return TRUE;\r
8683 }\r
8684 \r
8685 void\r
8686 StartClockTimer(long millisec)\r
8687 {\r
8688   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8689                              (UINT) millisec, NULL);\r
8690 }\r
8691 \r
8692 void\r
8693 DisplayWhiteClock(long timeRemaining, int highlight)\r
8694 {\r
8695   HDC hdc;\r
8696   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8697 \r
8698   if(appData.noGUI) return;\r
8699   hdc = GetDC(hwndMain);\r
8700   if (!IsIconic(hwndMain)) {\r
8701     DisplayAClock(hdc, timeRemaining, highlight, \r
8702                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8703   }\r
8704   if (highlight && iconCurrent == iconBlack) {\r
8705     iconCurrent = iconWhite;\r
8706     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8707     if (IsIconic(hwndMain)) {\r
8708       DrawIcon(hdc, 2, 2, iconCurrent);\r
8709     }\r
8710   }\r
8711   (void) ReleaseDC(hwndMain, hdc);\r
8712   if (hwndConsole)\r
8713     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8714 }\r
8715 \r
8716 void\r
8717 DisplayBlackClock(long timeRemaining, int highlight)\r
8718 {\r
8719   HDC hdc;\r
8720   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8721 \r
8722   if(appData.noGUI) return;\r
8723   hdc = GetDC(hwndMain);\r
8724   if (!IsIconic(hwndMain)) {\r
8725     DisplayAClock(hdc, timeRemaining, highlight, \r
8726                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8727   }\r
8728   if (highlight && iconCurrent == iconWhite) {\r
8729     iconCurrent = iconBlack;\r
8730     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8731     if (IsIconic(hwndMain)) {\r
8732       DrawIcon(hdc, 2, 2, iconCurrent);\r
8733     }\r
8734   }\r
8735   (void) ReleaseDC(hwndMain, hdc);\r
8736   if (hwndConsole)\r
8737     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8738 }\r
8739 \r
8740 \r
8741 int\r
8742 LoadGameTimerRunning()\r
8743 {\r
8744   return loadGameTimerEvent != 0;\r
8745 }\r
8746 \r
8747 int\r
8748 StopLoadGameTimer()\r
8749 {\r
8750   if (loadGameTimerEvent == 0) return FALSE;\r
8751   KillTimer(hwndMain, loadGameTimerEvent);\r
8752   loadGameTimerEvent = 0;\r
8753   return TRUE;\r
8754 }\r
8755 \r
8756 void\r
8757 StartLoadGameTimer(long millisec)\r
8758 {\r
8759   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8760                                 (UINT) millisec, NULL);\r
8761 }\r
8762 \r
8763 void\r
8764 AutoSaveGame()\r
8765 {\r
8766   char *defName;\r
8767   FILE *f;\r
8768   char fileTitle[MSG_SIZ];\r
8769 \r
8770   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8771   f = OpenFileDialog(hwndMain, "a", defName,\r
8772                      appData.oldSaveStyle ? "gam" : "pgn",\r
8773                      GAME_FILT, \r
8774                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8775   if (f != NULL) {\r
8776     SaveGame(f, 0, "");\r
8777     fclose(f);\r
8778   }\r
8779 }\r
8780 \r
8781 \r
8782 void\r
8783 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8784 {\r
8785   if (delayedTimerEvent != 0) {\r
8786     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8787       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8788     }\r
8789     KillTimer(hwndMain, delayedTimerEvent);\r
8790     delayedTimerEvent = 0;\r
8791     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8792     delayedTimerCallback();\r
8793   }\r
8794   delayedTimerCallback = cb;\r
8795   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8796                                 (UINT) millisec, NULL);\r
8797 }\r
8798 \r
8799 DelayedEventCallback\r
8800 GetDelayedEvent()\r
8801 {\r
8802   if (delayedTimerEvent) {\r
8803     return delayedTimerCallback;\r
8804   } else {\r
8805     return NULL;\r
8806   }\r
8807 }\r
8808 \r
8809 void\r
8810 CancelDelayedEvent()\r
8811 {\r
8812   if (delayedTimerEvent) {\r
8813     KillTimer(hwndMain, delayedTimerEvent);\r
8814     delayedTimerEvent = 0;\r
8815   }\r
8816 }\r
8817 \r
8818 DWORD GetWin32Priority(int nice)\r
8819 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8820 /*\r
8821 REALTIME_PRIORITY_CLASS     0x00000100\r
8822 HIGH_PRIORITY_CLASS         0x00000080\r
8823 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8824 NORMAL_PRIORITY_CLASS       0x00000020\r
8825 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8826 IDLE_PRIORITY_CLASS         0x00000040\r
8827 */\r
8828         if (nice < -15) return 0x00000080;\r
8829         if (nice < 0)   return 0x00008000;\r
8830         if (nice == 0)  return 0x00000020;\r
8831         if (nice < 15)  return 0x00004000;\r
8832         return 0x00000040;\r
8833 }\r
8834 \r
8835 /* Start a child process running the given program.\r
8836    The process's standard output can be read from "from", and its\r
8837    standard input can be written to "to".\r
8838    Exit with fatal error if anything goes wrong.\r
8839    Returns an opaque pointer that can be used to destroy the process\r
8840    later.\r
8841 */\r
8842 int\r
8843 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8844 {\r
8845 #define BUFSIZE 4096\r
8846 \r
8847   HANDLE hChildStdinRd, hChildStdinWr,\r
8848     hChildStdoutRd, hChildStdoutWr;\r
8849   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8850   SECURITY_ATTRIBUTES saAttr;\r
8851   BOOL fSuccess;\r
8852   PROCESS_INFORMATION piProcInfo;\r
8853   STARTUPINFO siStartInfo;\r
8854   ChildProc *cp;\r
8855   char buf[MSG_SIZ];\r
8856   DWORD err;\r
8857 \r
8858   if (appData.debugMode) {\r
8859     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8860   }\r
8861 \r
8862   *pr = NoProc;\r
8863 \r
8864   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8865   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8866   saAttr.bInheritHandle = TRUE;\r
8867   saAttr.lpSecurityDescriptor = NULL;\r
8868 \r
8869   /*\r
8870    * The steps for redirecting child's STDOUT:\r
8871    *     1. Create anonymous pipe to be STDOUT for child.\r
8872    *     2. Create a noninheritable duplicate of read handle,\r
8873    *         and close the inheritable read handle.\r
8874    */\r
8875 \r
8876   /* Create a pipe for the child's STDOUT. */\r
8877   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8878     return GetLastError();\r
8879   }\r
8880 \r
8881   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8882   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8883                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8884                              FALSE,     /* not inherited */\r
8885                              DUPLICATE_SAME_ACCESS);\r
8886   if (! fSuccess) {\r
8887     return GetLastError();\r
8888   }\r
8889   CloseHandle(hChildStdoutRd);\r
8890 \r
8891   /*\r
8892    * The steps for redirecting child's STDIN:\r
8893    *     1. Create anonymous pipe to be STDIN for child.\r
8894    *     2. Create a noninheritable duplicate of write handle,\r
8895    *         and close the inheritable write handle.\r
8896    */\r
8897 \r
8898   /* Create a pipe for the child's STDIN. */\r
8899   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8900     return GetLastError();\r
8901   }\r
8902 \r
8903   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8904   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8905                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8906                              FALSE,     /* not inherited */\r
8907                              DUPLICATE_SAME_ACCESS);\r
8908   if (! fSuccess) {\r
8909     return GetLastError();\r
8910   }\r
8911   CloseHandle(hChildStdinWr);\r
8912 \r
8913   /* Arrange to (1) look in dir for the child .exe file, and\r
8914    * (2) have dir be the child's working directory.  Interpret\r
8915    * dir relative to the directory WinBoard loaded from. */\r
8916   GetCurrentDirectory(MSG_SIZ, buf);\r
8917   SetCurrentDirectory(installDir);\r
8918   SetCurrentDirectory(dir);\r
8919 \r
8920   /* Now create the child process. */\r
8921 \r
8922   siStartInfo.cb = sizeof(STARTUPINFO);\r
8923   siStartInfo.lpReserved = NULL;\r
8924   siStartInfo.lpDesktop = NULL;\r
8925   siStartInfo.lpTitle = NULL;\r
8926   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8927   siStartInfo.cbReserved2 = 0;\r
8928   siStartInfo.lpReserved2 = NULL;\r
8929   siStartInfo.hStdInput = hChildStdinRd;\r
8930   siStartInfo.hStdOutput = hChildStdoutWr;\r
8931   siStartInfo.hStdError = hChildStdoutWr;\r
8932 \r
8933   fSuccess = CreateProcess(NULL,\r
8934                            cmdLine,        /* command line */\r
8935                            NULL,           /* process security attributes */\r
8936                            NULL,           /* primary thread security attrs */\r
8937                            TRUE,           /* handles are inherited */\r
8938                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8939                            NULL,           /* use parent's environment */\r
8940                            NULL,\r
8941                            &siStartInfo, /* STARTUPINFO pointer */\r
8942                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
8943 \r
8944   err = GetLastError();\r
8945   SetCurrentDirectory(buf); /* return to prev directory */\r
8946   if (! fSuccess) {\r
8947     return err;\r
8948   }\r
8949 \r
8950   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
8951     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
8952     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
8953   }\r
8954 \r
8955   /* Close the handles we don't need in the parent */\r
8956   CloseHandle(piProcInfo.hThread);\r
8957   CloseHandle(hChildStdinRd);\r
8958   CloseHandle(hChildStdoutWr);\r
8959 \r
8960   /* Prepare return value */\r
8961   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8962   cp->kind = CPReal;\r
8963   cp->hProcess = piProcInfo.hProcess;\r
8964   cp->pid = piProcInfo.dwProcessId;\r
8965   cp->hFrom = hChildStdoutRdDup;\r
8966   cp->hTo = hChildStdinWrDup;\r
8967 \r
8968   *pr = (void *) cp;\r
8969 \r
8970   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
8971      2000 where engines sometimes don't see the initial command(s)\r
8972      from WinBoard and hang.  I don't understand how that can happen,\r
8973      but the Sleep is harmless, so I've put it in.  Others have also\r
8974      reported what may be the same problem, so hopefully this will fix\r
8975      it for them too.  */\r
8976   Sleep(500);\r
8977 \r
8978   return NO_ERROR;\r
8979 }\r
8980 \r
8981 \r
8982 void\r
8983 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
8984 {\r
8985   ChildProc *cp; int result;\r
8986 \r
8987   cp = (ChildProc *) pr;\r
8988   if (cp == NULL) return;\r
8989 \r
8990   switch (cp->kind) {\r
8991   case CPReal:\r
8992     /* TerminateProcess is considered harmful, so... */\r
8993     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
8994     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
8995     /* The following doesn't work because the chess program\r
8996        doesn't "have the same console" as WinBoard.  Maybe\r
8997        we could arrange for this even though neither WinBoard\r
8998        nor the chess program uses a console for stdio? */\r
8999     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9000 \r
9001     /* [AS] Special termination modes for misbehaving programs... */\r
9002     if( signal == 9 ) { \r
9003         result = TerminateProcess( cp->hProcess, 0 );\r
9004 \r
9005         if ( appData.debugMode) {\r
9006             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9007         }\r
9008     }\r
9009     else if( signal == 10 ) {\r
9010         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9011 \r
9012         if( dw != WAIT_OBJECT_0 ) {\r
9013             result = TerminateProcess( cp->hProcess, 0 );\r
9014 \r
9015             if ( appData.debugMode) {\r
9016                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9017             }\r
9018 \r
9019         }\r
9020     }\r
9021 \r
9022     CloseHandle(cp->hProcess);\r
9023     break;\r
9024 \r
9025   case CPComm:\r
9026     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9027     break;\r
9028 \r
9029   case CPSock:\r
9030     closesocket(cp->sock);\r
9031     WSACleanup();\r
9032     break;\r
9033 \r
9034   case CPRcmd:\r
9035     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9036     closesocket(cp->sock);\r
9037     closesocket(cp->sock2);\r
9038     WSACleanup();\r
9039     break;\r
9040   }\r
9041   free(cp);\r
9042 }\r
9043 \r
9044 void\r
9045 InterruptChildProcess(ProcRef pr)\r
9046 {\r
9047   ChildProc *cp;\r
9048 \r
9049   cp = (ChildProc *) pr;\r
9050   if (cp == NULL) return;\r
9051   switch (cp->kind) {\r
9052   case CPReal:\r
9053     /* The following doesn't work because the chess program\r
9054        doesn't "have the same console" as WinBoard.  Maybe\r
9055        we could arrange for this even though neither WinBoard\r
9056        nor the chess program uses a console for stdio */\r
9057     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9058     break;\r
9059 \r
9060   case CPComm:\r
9061   case CPSock:\r
9062     /* Can't interrupt */\r
9063     break;\r
9064 \r
9065   case CPRcmd:\r
9066     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9067     break;\r
9068   }\r
9069 }\r
9070 \r
9071 \r
9072 int\r
9073 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9074 {\r
9075   char cmdLine[MSG_SIZ];\r
9076 \r
9077   if (port[0] == NULLCHAR) {\r
9078     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9079   } else {\r
9080     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9081   }\r
9082   return StartChildProcess(cmdLine, "", pr);\r
9083 }\r
9084 \r
9085 \r
9086 /* Code to open TCP sockets */\r
9087 \r
9088 int\r
9089 OpenTCP(char *host, char *port, ProcRef *pr)\r
9090 {\r
9091   ChildProc *cp;\r
9092   int err;\r
9093   SOCKET s;\r
9094   struct sockaddr_in sa, mysa;\r
9095   struct hostent FAR *hp;\r
9096   unsigned short uport;\r
9097   WORD wVersionRequested;\r
9098   WSADATA wsaData;\r
9099 \r
9100   /* Initialize socket DLL */\r
9101   wVersionRequested = MAKEWORD(1, 1);\r
9102   err = WSAStartup(wVersionRequested, &wsaData);\r
9103   if (err != 0) return err;\r
9104 \r
9105   /* Make socket */\r
9106   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9107     err = WSAGetLastError();\r
9108     WSACleanup();\r
9109     return err;\r
9110   }\r
9111 \r
9112   /* Bind local address using (mostly) don't-care values.\r
9113    */\r
9114   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9115   mysa.sin_family = AF_INET;\r
9116   mysa.sin_addr.s_addr = INADDR_ANY;\r
9117   uport = (unsigned short) 0;\r
9118   mysa.sin_port = htons(uport);\r
9119   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9120       == SOCKET_ERROR) {\r
9121     err = WSAGetLastError();\r
9122     WSACleanup();\r
9123     return err;\r
9124   }\r
9125 \r
9126   /* Resolve remote host name */\r
9127   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9128   if (!(hp = gethostbyname(host))) {\r
9129     unsigned int b0, b1, b2, b3;\r
9130 \r
9131     err = WSAGetLastError();\r
9132 \r
9133     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9134       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9135       hp->h_addrtype = AF_INET;\r
9136       hp->h_length = 4;\r
9137       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9138       hp->h_addr_list[0] = (char *) malloc(4);\r
9139       hp->h_addr_list[0][0] = (char) b0;\r
9140       hp->h_addr_list[0][1] = (char) b1;\r
9141       hp->h_addr_list[0][2] = (char) b2;\r
9142       hp->h_addr_list[0][3] = (char) b3;\r
9143     } else {\r
9144       WSACleanup();\r
9145       return err;\r
9146     }\r
9147   }\r
9148   sa.sin_family = hp->h_addrtype;\r
9149   uport = (unsigned short) atoi(port);\r
9150   sa.sin_port = htons(uport);\r
9151   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9152 \r
9153   /* Make connection */\r
9154   if (connect(s, (struct sockaddr *) &sa,\r
9155               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9156     err = WSAGetLastError();\r
9157     WSACleanup();\r
9158     return err;\r
9159   }\r
9160 \r
9161   /* Prepare return value */\r
9162   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9163   cp->kind = CPSock;\r
9164   cp->sock = s;\r
9165   *pr = (ProcRef *) cp;\r
9166 \r
9167   return NO_ERROR;\r
9168 }\r
9169 \r
9170 int\r
9171 OpenCommPort(char *name, ProcRef *pr)\r
9172 {\r
9173   HANDLE h;\r
9174   COMMTIMEOUTS ct;\r
9175   ChildProc *cp;\r
9176   char fullname[MSG_SIZ];\r
9177 \r
9178   if (*name != '\\')\r
9179     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9180   else\r
9181     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9182 \r
9183   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9184                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9185   if (h == (HANDLE) -1) {\r
9186     return GetLastError();\r
9187   }\r
9188   hCommPort = h;\r
9189 \r
9190   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9191 \r
9192   /* Accumulate characters until a 100ms pause, then parse */\r
9193   ct.ReadIntervalTimeout = 100;\r
9194   ct.ReadTotalTimeoutMultiplier = 0;\r
9195   ct.ReadTotalTimeoutConstant = 0;\r
9196   ct.WriteTotalTimeoutMultiplier = 0;\r
9197   ct.WriteTotalTimeoutConstant = 0;\r
9198   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9199 \r
9200   /* Prepare return value */\r
9201   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9202   cp->kind = CPComm;\r
9203   cp->hFrom = h;\r
9204   cp->hTo = h;\r
9205   *pr = (ProcRef *) cp;\r
9206 \r
9207   return NO_ERROR;\r
9208 }\r
9209 \r
9210 int\r
9211 OpenLoopback(ProcRef *pr)\r
9212 {\r
9213   DisplayFatalError(_("Not implemented"), 0, 1);\r
9214   return NO_ERROR;\r
9215 }\r
9216 \r
9217 \r
9218 int\r
9219 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9220 {\r
9221   ChildProc *cp;\r
9222   int err;\r
9223   SOCKET s, s2, s3;\r
9224   struct sockaddr_in sa, mysa;\r
9225   struct hostent FAR *hp;\r
9226   unsigned short uport;\r
9227   WORD wVersionRequested;\r
9228   WSADATA wsaData;\r
9229   int fromPort;\r
9230   char stderrPortStr[MSG_SIZ];\r
9231 \r
9232   /* Initialize socket DLL */\r
9233   wVersionRequested = MAKEWORD(1, 1);\r
9234   err = WSAStartup(wVersionRequested, &wsaData);\r
9235   if (err != 0) return err;\r
9236 \r
9237   /* Resolve remote host name */\r
9238   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9239   if (!(hp = gethostbyname(host))) {\r
9240     unsigned int b0, b1, b2, b3;\r
9241 \r
9242     err = WSAGetLastError();\r
9243 \r
9244     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9245       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9246       hp->h_addrtype = AF_INET;\r
9247       hp->h_length = 4;\r
9248       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9249       hp->h_addr_list[0] = (char *) malloc(4);\r
9250       hp->h_addr_list[0][0] = (char) b0;\r
9251       hp->h_addr_list[0][1] = (char) b1;\r
9252       hp->h_addr_list[0][2] = (char) b2;\r
9253       hp->h_addr_list[0][3] = (char) b3;\r
9254     } else {\r
9255       WSACleanup();\r
9256       return err;\r
9257     }\r
9258   }\r
9259   sa.sin_family = hp->h_addrtype;\r
9260   uport = (unsigned short) 514;\r
9261   sa.sin_port = htons(uport);\r
9262   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9263 \r
9264   /* Bind local socket to unused "privileged" port address\r
9265    */\r
9266   s = INVALID_SOCKET;\r
9267   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9268   mysa.sin_family = AF_INET;\r
9269   mysa.sin_addr.s_addr = INADDR_ANY;\r
9270   for (fromPort = 1023;; fromPort--) {\r
9271     if (fromPort < 0) {\r
9272       WSACleanup();\r
9273       return WSAEADDRINUSE;\r
9274     }\r
9275     if (s == INVALID_SOCKET) {\r
9276       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9277         err = WSAGetLastError();\r
9278         WSACleanup();\r
9279         return err;\r
9280       }\r
9281     }\r
9282     uport = (unsigned short) fromPort;\r
9283     mysa.sin_port = htons(uport);\r
9284     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9285         == SOCKET_ERROR) {\r
9286       err = WSAGetLastError();\r
9287       if (err == WSAEADDRINUSE) continue;\r
9288       WSACleanup();\r
9289       return err;\r
9290     }\r
9291     if (connect(s, (struct sockaddr *) &sa,\r
9292       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9293       err = WSAGetLastError();\r
9294       if (err == WSAEADDRINUSE) {\r
9295         closesocket(s);\r
9296         s = -1;\r
9297         continue;\r
9298       }\r
9299       WSACleanup();\r
9300       return err;\r
9301     }\r
9302     break;\r
9303   }\r
9304 \r
9305   /* Bind stderr local socket to unused "privileged" port address\r
9306    */\r
9307   s2 = INVALID_SOCKET;\r
9308   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9309   mysa.sin_family = AF_INET;\r
9310   mysa.sin_addr.s_addr = INADDR_ANY;\r
9311   for (fromPort = 1023;; fromPort--) {\r
9312     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9313     if (fromPort < 0) {\r
9314       (void) closesocket(s);\r
9315       WSACleanup();\r
9316       return WSAEADDRINUSE;\r
9317     }\r
9318     if (s2 == INVALID_SOCKET) {\r
9319       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9320         err = WSAGetLastError();\r
9321         closesocket(s);\r
9322         WSACleanup();\r
9323         return err;\r
9324       }\r
9325     }\r
9326     uport = (unsigned short) fromPort;\r
9327     mysa.sin_port = htons(uport);\r
9328     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9329         == SOCKET_ERROR) {\r
9330       err = WSAGetLastError();\r
9331       if (err == WSAEADDRINUSE) continue;\r
9332       (void) closesocket(s);\r
9333       WSACleanup();\r
9334       return err;\r
9335     }\r
9336     if (listen(s2, 1) == SOCKET_ERROR) {\r
9337       err = WSAGetLastError();\r
9338       if (err == WSAEADDRINUSE) {\r
9339         closesocket(s2);\r
9340         s2 = INVALID_SOCKET;\r
9341         continue;\r
9342       }\r
9343       (void) closesocket(s);\r
9344       (void) closesocket(s2);\r
9345       WSACleanup();\r
9346       return err;\r
9347     }\r
9348     break;\r
9349   }\r
9350   prevStderrPort = fromPort; // remember port used\r
9351   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9352 \r
9353   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9354     err = WSAGetLastError();\r
9355     (void) closesocket(s);\r
9356     (void) closesocket(s2);\r
9357     WSACleanup();\r
9358     return err;\r
9359   }\r
9360 \r
9361   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9362     err = WSAGetLastError();\r
9363     (void) closesocket(s);\r
9364     (void) closesocket(s2);\r
9365     WSACleanup();\r
9366     return err;\r
9367   }\r
9368   if (*user == NULLCHAR) user = UserName();\r
9369   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9370     err = WSAGetLastError();\r
9371     (void) closesocket(s);\r
9372     (void) closesocket(s2);\r
9373     WSACleanup();\r
9374     return err;\r
9375   }\r
9376   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9377     err = WSAGetLastError();\r
9378     (void) closesocket(s);\r
9379     (void) closesocket(s2);\r
9380     WSACleanup();\r
9381     return err;\r
9382   }\r
9383 \r
9384   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9385     err = WSAGetLastError();\r
9386     (void) closesocket(s);\r
9387     (void) closesocket(s2);\r
9388     WSACleanup();\r
9389     return err;\r
9390   }\r
9391   (void) closesocket(s2);  /* Stop listening */\r
9392 \r
9393   /* Prepare return value */\r
9394   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9395   cp->kind = CPRcmd;\r
9396   cp->sock = s;\r
9397   cp->sock2 = s3;\r
9398   *pr = (ProcRef *) cp;\r
9399 \r
9400   return NO_ERROR;\r
9401 }\r
9402 \r
9403 \r
9404 InputSourceRef\r
9405 AddInputSource(ProcRef pr, int lineByLine,\r
9406                InputCallback func, VOIDSTAR closure)\r
9407 {\r
9408   InputSource *is, *is2 = NULL;\r
9409   ChildProc *cp = (ChildProc *) pr;\r
9410 \r
9411   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9412   is->lineByLine = lineByLine;\r
9413   is->func = func;\r
9414   is->closure = closure;\r
9415   is->second = NULL;\r
9416   is->next = is->buf;\r
9417   if (pr == NoProc) {\r
9418     is->kind = CPReal;\r
9419     consoleInputSource = is;\r
9420   } else {\r
9421     is->kind = cp->kind;\r
9422     /* \r
9423         [AS] Try to avoid a race condition if the thread is given control too early:\r
9424         we create all threads suspended so that the is->hThread variable can be\r
9425         safely assigned, then let the threads start with ResumeThread.\r
9426     */\r
9427     switch (cp->kind) {\r
9428     case CPReal:\r
9429       is->hFile = cp->hFrom;\r
9430       cp->hFrom = NULL; /* now owned by InputThread */\r
9431       is->hThread =\r
9432         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9433                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9434       break;\r
9435 \r
9436     case CPComm:\r
9437       is->hFile = cp->hFrom;\r
9438       cp->hFrom = NULL; /* now owned by InputThread */\r
9439       is->hThread =\r
9440         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9441                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9442       break;\r
9443 \r
9444     case CPSock:\r
9445       is->sock = cp->sock;\r
9446       is->hThread =\r
9447         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9448                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9449       break;\r
9450 \r
9451     case CPRcmd:\r
9452       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9453       *is2 = *is;\r
9454       is->sock = cp->sock;\r
9455       is->second = is2;\r
9456       is2->sock = cp->sock2;\r
9457       is2->second = is2;\r
9458       is->hThread =\r
9459         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9460                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9461       is2->hThread =\r
9462         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9463                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9464       break;\r
9465     }\r
9466 \r
9467     if( is->hThread != NULL ) {\r
9468         ResumeThread( is->hThread );\r
9469     }\r
9470 \r
9471     if( is2 != NULL && is2->hThread != NULL ) {\r
9472         ResumeThread( is2->hThread );\r
9473     }\r
9474   }\r
9475 \r
9476   return (InputSourceRef) is;\r
9477 }\r
9478 \r
9479 void\r
9480 RemoveInputSource(InputSourceRef isr)\r
9481 {\r
9482   InputSource *is;\r
9483 \r
9484   is = (InputSource *) isr;\r
9485   is->hThread = NULL;  /* tell thread to stop */\r
9486   CloseHandle(is->hThread);\r
9487   if (is->second != NULL) {\r
9488     is->second->hThread = NULL;\r
9489     CloseHandle(is->second->hThread);\r
9490   }\r
9491 }\r
9492 \r
9493 int no_wrap(char *message, int count)\r
9494 {\r
9495     ConsoleOutput(message, count, FALSE);\r
9496     return count;\r
9497 }\r
9498 \r
9499 int\r
9500 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9501 {\r
9502   DWORD dOutCount;\r
9503   int outCount = SOCKET_ERROR;\r
9504   ChildProc *cp = (ChildProc *) pr;\r
9505   static OVERLAPPED ovl;\r
9506   static int line = 0;\r
9507 \r
9508   if (pr == NoProc)\r
9509   {\r
9510     if (appData.noJoin || !appData.useInternalWrap)\r
9511       return no_wrap(message, count);\r
9512     else\r
9513     {\r
9514       int width = get_term_width();\r
9515       int len = wrap(NULL, message, count, width, &line);\r
9516       char *msg = malloc(len);\r
9517       int dbgchk;\r
9518 \r
9519       if (!msg)\r
9520         return no_wrap(message, count);\r
9521       else\r
9522       {\r
9523         dbgchk = wrap(msg, message, count, width, &line);\r
9524         if (dbgchk != len && appData.debugMode)\r
9525             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9526         ConsoleOutput(msg, len, FALSE);\r
9527         free(msg);\r
9528         return len;\r
9529       }\r
9530     }\r
9531   }\r
9532 \r
9533   if (ovl.hEvent == NULL) {\r
9534     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9535   }\r
9536   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9537 \r
9538   switch (cp->kind) {\r
9539   case CPSock:\r
9540   case CPRcmd:\r
9541     outCount = send(cp->sock, message, count, 0);\r
9542     if (outCount == SOCKET_ERROR) {\r
9543       *outError = WSAGetLastError();\r
9544     } else {\r
9545       *outError = NO_ERROR;\r
9546     }\r
9547     break;\r
9548 \r
9549   case CPReal:\r
9550     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9551                   &dOutCount, NULL)) {\r
9552       *outError = NO_ERROR;\r
9553       outCount = (int) dOutCount;\r
9554     } else {\r
9555       *outError = GetLastError();\r
9556     }\r
9557     break;\r
9558 \r
9559   case CPComm:\r
9560     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9561                             &dOutCount, &ovl);\r
9562     if (*outError == NO_ERROR) {\r
9563       outCount = (int) dOutCount;\r
9564     }\r
9565     break;\r
9566   }\r
9567   return outCount;\r
9568 }\r
9569 \r
9570 int\r
9571 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9572                        long msdelay)\r
9573 {\r
9574   /* Ignore delay, not implemented for WinBoard */\r
9575   return OutputToProcess(pr, message, count, outError);\r
9576 }\r
9577 \r
9578 \r
9579 void\r
9580 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9581                         char *buf, int count, int error)\r
9582 {\r
9583   DisplayFatalError(_("Not implemented"), 0, 1);\r
9584 }\r
9585 \r
9586 /* see wgamelist.c for Game List functions */\r
9587 /* see wedittags.c for Edit Tags functions */\r
9588 \r
9589 \r
9590 VOID\r
9591 ICSInitScript()\r
9592 {\r
9593   FILE *f;\r
9594   char buf[MSG_SIZ];\r
9595   char *dummy;\r
9596 \r
9597   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9598     f = fopen(buf, "r");\r
9599     if (f != NULL) {\r
9600       ProcessICSInitScript(f);\r
9601       fclose(f);\r
9602     }\r
9603   }\r
9604 }\r
9605 \r
9606 \r
9607 VOID\r
9608 StartAnalysisClock()\r
9609 {\r
9610   if (analysisTimerEvent) return;\r
9611   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9612                                         (UINT) 2000, NULL);\r
9613 }\r
9614 \r
9615 VOID\r
9616 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9617 {\r
9618   highlightInfo.sq[0].x = fromX;\r
9619   highlightInfo.sq[0].y = fromY;\r
9620   highlightInfo.sq[1].x = toX;\r
9621   highlightInfo.sq[1].y = toY;\r
9622 }\r
9623 \r
9624 VOID\r
9625 ClearHighlights()\r
9626 {\r
9627   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9628     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9629 }\r
9630 \r
9631 VOID\r
9632 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9633 {\r
9634   premoveHighlightInfo.sq[0].x = fromX;\r
9635   premoveHighlightInfo.sq[0].y = fromY;\r
9636   premoveHighlightInfo.sq[1].x = toX;\r
9637   premoveHighlightInfo.sq[1].y = toY;\r
9638 }\r
9639 \r
9640 VOID\r
9641 ClearPremoveHighlights()\r
9642 {\r
9643   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9644     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9645 }\r
9646 \r
9647 VOID\r
9648 ShutDownFrontEnd()\r
9649 {\r
9650   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9651   DeleteClipboardTempFiles();\r
9652 }\r
9653 \r
9654 void\r
9655 BoardToTop()\r
9656 {\r
9657     if (IsIconic(hwndMain))\r
9658       ShowWindow(hwndMain, SW_RESTORE);\r
9659 \r
9660     SetActiveWindow(hwndMain);\r
9661 }\r
9662 \r
9663 /*\r
9664  * Prototypes for animation support routines\r
9665  */\r
9666 static void ScreenSquare(int column, int row, POINT * pt);\r
9667 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9668      POINT frames[], int * nFrames);\r
9669 \r
9670 \r
9671 #define kFactor 4\r
9672 \r
9673 void\r
9674 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9675 {       // [HGM] atomic: animate blast wave\r
9676         int i;\r
9677 \r
9678         explodeInfo.fromX = fromX;\r
9679         explodeInfo.fromY = fromY;\r
9680         explodeInfo.toX = toX;\r
9681         explodeInfo.toY = toY;\r
9682         for(i=1; i<4*kFactor; i++) {\r
9683             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9684             DrawPosition(FALSE, board);\r
9685             Sleep(appData.animSpeed);\r
9686         }\r
9687         explodeInfo.radius = 0;\r
9688         DrawPosition(TRUE, board);\r
9689 }\r
9690 \r
9691 void\r
9692 AnimateMove(board, fromX, fromY, toX, toY)\r
9693      Board board;\r
9694      int fromX;\r
9695      int fromY;\r
9696      int toX;\r
9697      int toY;\r
9698 {\r
9699   ChessSquare piece;\r
9700   POINT start, finish, mid;\r
9701   POINT frames[kFactor * 2 + 1];\r
9702   int nFrames, n;\r
9703 \r
9704   if (!appData.animate) return;\r
9705   if (doingSizing) return;\r
9706   if (fromY < 0 || fromX < 0) return;\r
9707   piece = board[fromY][fromX];\r
9708   if (piece >= EmptySquare) return;\r
9709 \r
9710   ScreenSquare(fromX, fromY, &start);\r
9711   ScreenSquare(toX, toY, &finish);\r
9712 \r
9713   /* All moves except knight jumps move in straight line */\r
9714   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9715     mid.x = start.x + (finish.x - start.x) / 2;\r
9716     mid.y = start.y + (finish.y - start.y) / 2;\r
9717   } else {\r
9718     /* Knight: make straight movement then diagonal */\r
9719     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9720        mid.x = start.x + (finish.x - start.x) / 2;\r
9721        mid.y = start.y;\r
9722      } else {\r
9723        mid.x = start.x;\r
9724        mid.y = start.y + (finish.y - start.y) / 2;\r
9725      }\r
9726   }\r
9727   \r
9728   /* Don't use as many frames for very short moves */\r
9729   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9730     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9731   else\r
9732     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9733 \r
9734   animInfo.from.x = fromX;\r
9735   animInfo.from.y = fromY;\r
9736   animInfo.to.x = toX;\r
9737   animInfo.to.y = toY;\r
9738   animInfo.lastpos = start;\r
9739   animInfo.piece = piece;\r
9740   for (n = 0; n < nFrames; n++) {\r
9741     animInfo.pos = frames[n];\r
9742     DrawPosition(FALSE, NULL);\r
9743     animInfo.lastpos = animInfo.pos;\r
9744     Sleep(appData.animSpeed);\r
9745   }\r
9746   animInfo.pos = finish;\r
9747   DrawPosition(FALSE, NULL);\r
9748   animInfo.piece = EmptySquare;\r
9749   Explode(board, fromX, fromY, toX, toY);\r
9750 }\r
9751 \r
9752 /*      Convert board position to corner of screen rect and color       */\r
9753 \r
9754 static void\r
9755 ScreenSquare(column, row, pt)\r
9756      int column; int row; POINT * pt;\r
9757 {\r
9758   if (flipView) {\r
9759     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9760     pt->y = lineGap + row * (squareSize + lineGap);\r
9761   } else {\r
9762     pt->x = lineGap + column * (squareSize + lineGap);\r
9763     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9764   }\r
9765 }\r
9766 \r
9767 /*      Generate a series of frame coords from start->mid->finish.\r
9768         The movement rate doubles until the half way point is\r
9769         reached, then halves back down to the final destination,\r
9770         which gives a nice slow in/out effect. The algorithmn\r
9771         may seem to generate too many intermediates for short\r
9772         moves, but remember that the purpose is to attract the\r
9773         viewers attention to the piece about to be moved and\r
9774         then to where it ends up. Too few frames would be less\r
9775         noticeable.                                             */\r
9776 \r
9777 static void\r
9778 Tween(start, mid, finish, factor, frames, nFrames)\r
9779      POINT * start; POINT * mid;\r
9780      POINT * finish; int factor;\r
9781      POINT frames[]; int * nFrames;\r
9782 {\r
9783   int n, fraction = 1, count = 0;\r
9784 \r
9785   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9786   for (n = 0; n < factor; n++)\r
9787     fraction *= 2;\r
9788   for (n = 0; n < factor; n++) {\r
9789     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9790     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9791     count ++;\r
9792     fraction = fraction / 2;\r
9793   }\r
9794   \r
9795   /* Midpoint */\r
9796   frames[count] = *mid;\r
9797   count ++;\r
9798   \r
9799   /* Slow out, stepping 1/2, then 1/4, ... */\r
9800   fraction = 2;\r
9801   for (n = 0; n < factor; n++) {\r
9802     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9803     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9804     count ++;\r
9805     fraction = fraction * 2;\r
9806   }\r
9807   *nFrames = count;\r
9808 }\r
9809 \r
9810 void\r
9811 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9812 {\r
9813     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9814 \r
9815     EvalGraphSet( first, last, current, pvInfoList );\r
9816 }\r
9817 \r
9818 void\r
9819 SettingsPopUp(ChessProgramState *cps)\r
9820 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9821       EngineOptionsPopup(savedHwnd, cps);\r
9822 }\r