Fix Edit Game/Position checkmarking in WinBoard
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts. \r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009, 2010 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 \r
77 #if __GNUC__\r
78 #include <errno.h>\r
79 #include <string.h>\r
80 #endif\r
81 \r
82 #include "common.h"\r
83 #include "frontend.h"\r
84 #include "backend.h"\r
85 #include "winboard.h"\r
86 #include "moves.h"\r
87 #include "wclipbrd.h"\r
88 #include "woptions.h"\r
89 #include "wsockerr.h"\r
90 #include "defaults.h"\r
91 #include "help.h"\r
92 #include "wsnap.h"\r
93 \r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
95 \r
96   int myrandom(void);\r
97   void mysrandom(unsigned int seed);\r
98 \r
99 extern int whiteFlag, blackFlag;\r
100 Boolean flipClock = FALSE;\r
101 extern HANDLE chatHandle[];\r
102 extern int ics_type;\r
103 \r
104 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
105 VOID NewVariantPopup(HWND hwnd);\r
106 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
107                    /*char*/int promoChar));\r
108 void DisplayMove P((int moveNumber));\r
109 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
110 void ChatPopUp P((char *s));\r
111 typedef struct {\r
112   ChessSquare piece;  \r
113   POINT pos;      /* window coordinates of current pos */\r
114   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
115   POINT from;     /* board coordinates of the piece's orig pos */\r
116   POINT to;       /* board coordinates of the piece's new pos */\r
117 } AnimInfo;\r
118 \r
119 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
120 \r
121 typedef struct {\r
122   POINT start;    /* window coordinates of start pos */\r
123   POINT pos;      /* window coordinates of current pos */\r
124   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
125   POINT from;     /* board coordinates of the piece's orig pos */\r
126 } DragInfo;\r
127 \r
128 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
129 \r
130 typedef struct {\r
131   POINT sq[2];    /* board coordinates of from, to squares */\r
132 } HighlightInfo;\r
133 \r
134 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
135 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
136 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
137 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
138 \r
139 typedef struct { // [HGM] atomic\r
140   int fromX, fromY, toX, toY, radius;\r
141 } ExplodeInfo;\r
142 \r
143 static ExplodeInfo explodeInfo;\r
144 \r
145 /* Window class names */\r
146 char szAppName[] = "WinBoard";\r
147 char szConsoleName[] = "WBConsole";\r
148 \r
149 /* Title bar text */\r
150 char szTitle[] = "WinBoard";\r
151 char szConsoleTitle[] = "I C S Interaction";\r
152 \r
153 char *programName;\r
154 char *settingsFileName;\r
155 Boolean saveSettingsOnExit;\r
156 char installDir[MSG_SIZ];\r
157 int errorExitStatus;\r
158 \r
159 BoardSize boardSize;\r
160 Boolean chessProgram;\r
161 //static int boardX, boardY;\r
162 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
163 int squareSize, lineGap, minorSize;\r
164 static int winW, winH;\r
165 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
166 static int logoHeight = 0;\r
167 static char messageText[MESSAGE_TEXT_MAX];\r
168 static int clockTimerEvent = 0;\r
169 static int loadGameTimerEvent = 0;\r
170 static int analysisTimerEvent = 0;\r
171 static DelayedEventCallback delayedTimerCallback;\r
172 static int delayedTimerEvent = 0;\r
173 static int buttonCount = 2;\r
174 char *icsTextMenuString;\r
175 char *icsNames;\r
176 char *firstChessProgramNames;\r
177 char *secondChessProgramNames;\r
178 \r
179 #define PALETTESIZE 256\r
180 \r
181 HINSTANCE hInst;          /* current instance */\r
182 Boolean alwaysOnTop = FALSE;\r
183 RECT boardRect;\r
184 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
185   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
186 HPALETTE hPal;\r
187 ColorClass currentColorClass;\r
188 \r
189 static HWND savedHwnd;\r
190 HWND hCommPort = NULL;    /* currently open comm port */\r
191 static HWND hwndPause;    /* pause button */\r
192 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
193 static HBRUSH lightSquareBrush, darkSquareBrush,\r
194   blackSquareBrush, /* [HGM] for band between board and holdings */\r
195   explodeBrush,     /* [HGM] atomic */\r
196   markerBrush,      /* [HGM] markers */\r
197   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
198 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
199 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
200 static HPEN gridPen = NULL;\r
201 static HPEN highlightPen = NULL;\r
202 static HPEN premovePen = NULL;\r
203 static NPLOGPALETTE pLogPal;\r
204 static BOOL paletteChanged = FALSE;\r
205 static HICON iconWhite, iconBlack, iconCurrent;\r
206 static int doingSizing = FALSE;\r
207 static int lastSizing = 0;\r
208 static int prevStderrPort;\r
209 static HBITMAP userLogo;\r
210 \r
211 static HBITMAP liteBackTexture = NULL;\r
212 static HBITMAP darkBackTexture = NULL;\r
213 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
214 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
215 static int backTextureSquareSize = 0;\r
216 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
217 \r
218 #if __GNUC__ && !defined(_winmajor)\r
219 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
220 #else\r
221 #if defined(_winmajor)\r
222 #define oldDialog (_winmajor < 4)\r
223 #else\r
224 #define oldDialog 0\r
225 #endif\r
226 #endif\r
227 \r
228 #define INTERNATIONAL\r
229 \r
230 #ifdef INTERNATIONAL\r
231 #  define _(s) T_(s)\r
232 #  define N_(s) s\r
233 #else\r
234 #  define _(s) s\r
235 #  define N_(s) s\r
236 #  define T_(s) s\r
237 #  define Translate(x, y)\r
238 #  define LoadLanguageFile(s)\r
239 #endif\r
240 \r
241 #ifdef INTERNATIONAL\r
242 \r
243 Boolean barbaric; // flag indicating if translation is needed\r
244 \r
245 // list of item numbers used in each dialog (used to alter language at run time)\r
246 \r
247 #define ABOUTBOX -1  /* not sure why these are needed */\r
248 #define ABOUTBOX2 -1\r
249 \r
250 int dialogItems[][40] = {\r
251 { ABOUTBOX, IDOK, 400 }, \r
252 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
253   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
254 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, IDOK, IDCANCEL }, \r
255 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
256   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
257 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
258 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
259   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
260 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
261 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
262   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
263 { ABOUTBOX2, IDC_ChessBoard }, \r
264 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
265   OPT_GameListClose, IDC_GameListDoFilter }, \r
266 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
267 { DLG_Error, IDOK }, \r
268 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
269   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
270 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
271 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
272   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
273   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
274 { DLG_IndexNumber, IDC_Index }, \r
275 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
276 { DLG_TypeInName, IDOK, IDCANCEL }, \r
277 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
278   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
279 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
280   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
281   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
282   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
283   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
284   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
285   OPT_HighlightMoveArrow, OPT_AutoLogo }, \r
286 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
287   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
288   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
289   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
290   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
291   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
292   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
293   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
294   GPB_General, GPB_Alarm }, \r
295 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
296   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
297   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
298   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
299   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
300   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
301   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
302   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size }, \r
303 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
304   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
305   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
306   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
307   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
308   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
309   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
310   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
311   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
312 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
313   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
314   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,\r
315   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
316 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
317 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
318   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
319 { DLG_MoveHistory }, \r
320 { DLG_EvalGraph }, \r
321 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
322 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
323 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
324   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
325   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
326   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
327 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
328   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
329   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
330 { 0 }\r
331 };\r
332 \r
333 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
334 static int lastChecked;\r
335 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
336 extern int tinyLayout;\r
337 extern char * menuBarText[][10];\r
338 \r
339 void\r
340 LoadLanguageFile(char *name)\r
341 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
342     FILE *f;\r
343     int i=0, j=0, n=0, k;\r
344     char buf[MSG_SIZ];\r
345 \r
346     if(!name || name[0] == NULLCHAR) return;\r
347       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
348     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
349     if((f = fopen(buf, "r")) == NULL) return;\r
350     while((k = fgetc(f)) != EOF) {\r
351         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
352         languageBuf[i] = k;\r
353         if(k == '\n') {\r
354             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
355                 char *p;\r
356                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
357                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
358                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
359                         english[j] = languageBuf + n + 1; *p = 0;\r
360                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
361 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
362                     }\r
363                 }\r
364             }\r
365             n = i + 1;\r
366         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
367             switch(k) {\r
368               case 'n': k = '\n'; break;\r
369               case 'r': k = '\r'; break;\r
370               case 't': k = '\t'; break;\r
371             }\r
372             languageBuf[--i] = k;\r
373         }\r
374         i++;\r
375     }\r
376     fclose(f);\r
377     barbaric = (j != 0);\r
378     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
379 }\r
380 \r
381 char *\r
382 T_(char *s)\r
383 {   // return the translation of the given string\r
384     // efficiency can be improved a lot...\r
385     int i=0;\r
386 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
387     if(!barbaric) return s;\r
388     if(!s) return ""; // sanity\r
389     while(english[i]) {\r
390         if(!strcmp(s, english[i])) return foreign[i];\r
391         i++;\r
392     }\r
393     return s;\r
394 }\r
395 \r
396 void\r
397 Translate(HWND hDlg, int dialogID)\r
398 {   // translate all text items in the given dialog\r
399     int i=0, j, k;\r
400     char buf[MSG_SIZ], *s;\r
401 //if(appData.debugMode) fprintf(debugFP, "Translate(%d)\n", dialogID);\r
402     if(!barbaric) return;\r
403     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
404     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
405     GetWindowText( hDlg, buf, MSG_SIZ );\r
406     s = T_(buf);\r
407 //if(appData.debugMode) fprintf(debugFP, "WindowText '%s' -> '%s'\n", buf, s);\r
408     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
409     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
410         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
411         if(strlen(buf) == 0) continue;\r
412         s = T_(buf);\r
413         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
414     }\r
415 }\r
416 \r
417 void\r
418 TranslateMenus(int addLanguage)\r
419 {\r
420     int i, j;\r
421     WIN32_FIND_DATA fileData;\r
422     HANDLE hFind;\r
423 #define IDM_English 1895\r
424     if(1) {\r
425         HMENU mainMenu = GetMenu(hwndMain);\r
426         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
427           HMENU subMenu = GetSubMenu(mainMenu, i);\r
428           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
429                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
430           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
431             char buf[MSG_SIZ];\r
432             UINT k = GetMenuItemID(subMenu, j);\r
433               if(menuText[i][j])
434                 safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) ); else {\r
435                 GetMenuString(subMenu, j, buf, MSG_SIZ, MF_BYPOSITION);\r
436                 menuText[i][j] = strdup(buf); // remember original on first change\r
437             }\r
438             if(buf[0] == NULLCHAR) continue;\r
439 //fprintf(debugFP, "menu(%d,%d) = %s (%08x, %08x) %d\n", i, j, buf, mainMenu, subMenu, k);\r
440             ModifyMenu(subMenu, j, MF_STRING|MF_BYPOSITION\r
441                                    |CheckMenuItem(subMenu, j, MF_BYPOSITION)\r
442                                    |EnableMenuItem(subMenu, j, MF_BYPOSITION), k, T_(buf));\r
443           }\r
444         }\r
445         DrawMenuBar(hwndMain);\r
446     }\r
447 \r
448     if(!addLanguage) return;\r
449     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
450         HMENU mainMenu = GetMenu(hwndMain);\r
451         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
452         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
453         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
454         i = 0; lastChecked = IDM_English;\r
455         do {\r
456             char *p, *q = fileData.cFileName;\r
457             int checkFlag = MF_UNCHECKED;\r
458             languageFile[i] = strdup(q);\r
459             if(barbaric && !strcmp(oldLanguage, q)) {\r
460                 checkFlag = MF_CHECKED;\r
461                 lastChecked = IDM_English + i + 1;\r
462                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
463             }\r
464             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
465             p = strstr(fileData.cFileName, ".lng");\r
466             if(p) *p = 0;\r
467             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
468         } while(FindNextFile(hFind, &fileData));\r
469         FindClose(hFind);\r
470     }\r
471 }\r
472 \r
473 #endif\r
474 \r
475 typedef struct {\r
476   char *name;\r
477   int squareSize;\r
478   int lineGap;\r
479   int smallLayout;\r
480   int tinyLayout;\r
481   int cliWidth, cliHeight;\r
482 } SizeInfo;\r
483 \r
484 SizeInfo sizeInfo[] = \r
485 {\r
486   { "tiny",     21, 0, 1, 1, 0, 0 },\r
487   { "teeny",    25, 1, 1, 1, 0, 0 },\r
488   { "dinky",    29, 1, 1, 1, 0, 0 },\r
489   { "petite",   33, 1, 1, 1, 0, 0 },\r
490   { "slim",     37, 2, 1, 0, 0, 0 },\r
491   { "small",    40, 2, 1, 0, 0, 0 },\r
492   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
493   { "middling", 49, 2, 0, 0, 0, 0 },\r
494   { "average",  54, 2, 0, 0, 0, 0 },\r
495   { "moderate", 58, 3, 0, 0, 0, 0 },\r
496   { "medium",   64, 3, 0, 0, 0, 0 },\r
497   { "bulky",    72, 3, 0, 0, 0, 0 },\r
498   { "large",    80, 3, 0, 0, 0, 0 },\r
499   { "big",      87, 3, 0, 0, 0, 0 },\r
500   { "huge",     95, 3, 0, 0, 0, 0 },\r
501   { "giant",    108, 3, 0, 0, 0, 0 },\r
502   { "colossal", 116, 4, 0, 0, 0, 0 },\r
503   { "titanic",  129, 4, 0, 0, 0, 0 },\r
504   { NULL, 0, 0, 0, 0, 0, 0 }\r
505 };\r
506 \r
507 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
508 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
509 {\r
510   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL) },\r
511   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL) },\r
512   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL) },\r
513   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL) },\r
514   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL) },\r
515   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL) },\r
516   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL) },\r
517   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL) },\r
518   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL) },\r
519   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL) },\r
520   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL) },\r
521   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL) },\r
522   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL) },\r
523   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL) },\r
524   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL) },\r
525   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL) },\r
526   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL) },\r
527   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL) },\r
528 };\r
529 \r
530 MyFont *font[NUM_SIZES][NUM_FONTS];\r
531 \r
532 typedef struct {\r
533   char *label;\r
534   int id;\r
535   HWND hwnd;\r
536   WNDPROC wndproc;\r
537 } MyButtonDesc;\r
538 \r
539 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
540 #define N_BUTTONS 5\r
541 \r
542 MyButtonDesc buttonDesc[N_BUTTONS] =\r
543 {\r
544   {"<<", IDM_ToStart, NULL, NULL},\r
545   {"<", IDM_Backward, NULL, NULL},\r
546   {"P", IDM_Pause, NULL, NULL},\r
547   {">", IDM_Forward, NULL, NULL},\r
548   {">>", IDM_ToEnd, NULL, NULL},\r
549 };\r
550 \r
551 int tinyLayout = 0, smallLayout = 0;\r
552 #define MENU_BAR_ITEMS 9\r
553 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
554   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
555   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
556 };\r
557 \r
558 \r
559 MySound sounds[(int)NSoundClasses];\r
560 MyTextAttribs textAttribs[(int)NColorClasses];\r
561 \r
562 MyColorizeAttribs colorizeAttribs[] = {\r
563   { (COLORREF)0, 0, N_("Shout Text") },\r
564   { (COLORREF)0, 0, N_("SShout/CShout") },\r
565   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
566   { (COLORREF)0, 0, N_("Channel Text") },\r
567   { (COLORREF)0, 0, N_("Kibitz Text") },\r
568   { (COLORREF)0, 0, N_("Tell Text") },\r
569   { (COLORREF)0, 0, N_("Challenge Text") },\r
570   { (COLORREF)0, 0, N_("Request Text") },\r
571   { (COLORREF)0, 0, N_("Seek Text") },\r
572   { (COLORREF)0, 0, N_("Normal Text") },\r
573   { (COLORREF)0, 0, N_("None") }\r
574 };\r
575 \r
576 \r
577 \r
578 static char *commentTitle;\r
579 static char *commentText;\r
580 static int commentIndex;\r
581 static Boolean editComment = FALSE;\r
582 \r
583 \r
584 char errorTitle[MSG_SIZ];\r
585 char errorMessage[2*MSG_SIZ];\r
586 HWND errorDialog = NULL;\r
587 BOOLEAN moveErrorMessageUp = FALSE;\r
588 BOOLEAN consoleEcho = TRUE;\r
589 CHARFORMAT consoleCF;\r
590 COLORREF consoleBackgroundColor;\r
591 \r
592 char *programVersion;\r
593 \r
594 #define CPReal 1\r
595 #define CPComm 2\r
596 #define CPSock 3\r
597 #define CPRcmd 4\r
598 typedef int CPKind;\r
599 \r
600 typedef struct {\r
601   CPKind kind;\r
602   HANDLE hProcess;\r
603   DWORD pid;\r
604   HANDLE hTo;\r
605   HANDLE hFrom;\r
606   SOCKET sock;\r
607   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
608 } ChildProc;\r
609 \r
610 #define INPUT_SOURCE_BUF_SIZE 4096\r
611 \r
612 typedef struct _InputSource {\r
613   CPKind kind;\r
614   HANDLE hFile;\r
615   SOCKET sock;\r
616   int lineByLine;\r
617   HANDLE hThread;\r
618   DWORD id;\r
619   char buf[INPUT_SOURCE_BUF_SIZE];\r
620   char *next;\r
621   DWORD count;\r
622   int error;\r
623   InputCallback func;\r
624   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
625   VOIDSTAR closure;\r
626 } InputSource;\r
627 \r
628 InputSource *consoleInputSource;\r
629 \r
630 DCB dcb;\r
631 \r
632 /* forward */\r
633 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
634 VOID ConsoleCreate();\r
635 LRESULT CALLBACK\r
636   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
637 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
638 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
639 VOID ParseCommSettings(char *arg, DCB *dcb);\r
640 LRESULT CALLBACK\r
641   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
642 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
643 void ParseIcsTextMenu(char *icsTextMenuString);\r
644 VOID PopUpMoveDialog(char firstchar);\r
645 VOID PopUpNameDialog(char firstchar);\r
646 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
647 \r
648 /* [AS] */\r
649 int NewGameFRC();\r
650 int GameListOptions();\r
651 \r
652 int dummy; // [HGM] for obsolete args\r
653 \r
654 HWND hwndMain = NULL;        /* root window*/\r
655 HWND hwndConsole = NULL;\r
656 HWND commentDialog = NULL;\r
657 HWND moveHistoryDialog = NULL;\r
658 HWND evalGraphDialog = NULL;\r
659 HWND engineOutputDialog = NULL;\r
660 HWND gameListDialog = NULL;\r
661 HWND editTagsDialog = NULL;\r
662 \r
663 int commentUp = FALSE;\r
664 \r
665 WindowPlacement wpMain;\r
666 WindowPlacement wpConsole;\r
667 WindowPlacement wpComment;\r
668 WindowPlacement wpMoveHistory;\r
669 WindowPlacement wpEvalGraph;\r
670 WindowPlacement wpEngineOutput;\r
671 WindowPlacement wpGameList;\r
672 WindowPlacement wpTags;\r
673 \r
674 VOID EngineOptionsPopup(); // [HGM] settings\r
675 \r
676 VOID GothicPopUp(char *title, VariantClass variant);\r
677 /*\r
678  * Setting "frozen" should disable all user input other than deleting\r
679  * the window.  We do this while engines are initializing themselves.\r
680  */\r
681 static int frozen = 0;\r
682 static int oldMenuItemState[MENU_BAR_ITEMS];\r
683 void FreezeUI()\r
684 {\r
685   HMENU hmenu;\r
686   int i;\r
687 \r
688   if (frozen) return;\r
689   frozen = 1;\r
690   hmenu = GetMenu(hwndMain);\r
691   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
692     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
693   }\r
694   DrawMenuBar(hwndMain);\r
695 }\r
696 \r
697 /* Undo a FreezeUI */\r
698 void ThawUI()\r
699 {\r
700   HMENU hmenu;\r
701   int i;\r
702 \r
703   if (!frozen) return;\r
704   frozen = 0;\r
705   hmenu = GetMenu(hwndMain);\r
706   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
707     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
708   }\r
709   DrawMenuBar(hwndMain);\r
710 }\r
711 \r
712 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
713 \r
714 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
715 #ifdef JAWS\r
716 #include "jaws.c"\r
717 #else\r
718 #define JAWS_INIT\r
719 #define JAWS_ARGS\r
720 #define JAWS_ALT_INTERCEPT\r
721 #define JAWS_KB_NAVIGATION\r
722 #define JAWS_MENU_ITEMS\r
723 #define JAWS_SILENCE\r
724 #define JAWS_REPLAY\r
725 #define JAWS_ACCEL\r
726 #define JAWS_COPYRIGHT\r
727 #define JAWS_DELETE(X) X\r
728 #define SAYMACHINEMOVE()\r
729 #define SAY(X)\r
730 #endif\r
731 \r
732 /*---------------------------------------------------------------------------*\\r
733  *\r
734  * WinMain\r
735  *\r
736 \*---------------------------------------------------------------------------*/\r
737 \r
738 int APIENTRY\r
739 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
740         LPSTR lpCmdLine, int nCmdShow)\r
741 {\r
742   MSG msg;\r
743   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
744 //  INITCOMMONCONTROLSEX ex;\r
745 \r
746   debugFP = stderr;\r
747 \r
748   LoadLibrary("RICHED32.DLL");\r
749   consoleCF.cbSize = sizeof(CHARFORMAT);\r
750 \r
751   if (!InitApplication(hInstance)) {\r
752     return (FALSE);\r
753   }\r
754   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
755     return (FALSE);\r
756   }\r
757 \r
758   JAWS_INIT\r
759 \r
760 //  InitCommonControlsEx(&ex);\r
761   InitCommonControls();\r
762 \r
763   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
764   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
765   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
766 \r
767   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
768 \r
769   while (GetMessage(&msg, /* message structure */\r
770                     NULL, /* handle of window receiving the message */\r
771                     0,    /* lowest message to examine */\r
772                     0))   /* highest message to examine */\r
773     {\r
774 \r
775       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
776         // [HGM] navigate: switch between all windows with tab\r
777         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
778         int i, currentElement = 0;\r
779 \r
780         // first determine what element of the chain we come from (if any)\r
781         if(appData.icsActive) {\r
782             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
783             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
784         }\r
785         if(engineOutputDialog && EngineOutputIsUp()) {\r
786             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
787             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
788         }\r
789         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
790             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
791         }\r
792         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
793         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
794         if(msg.hwnd == e1)                 currentElement = 2; else\r
795         if(msg.hwnd == e2)                 currentElement = 3; else\r
796         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
797         if(msg.hwnd == mh)                currentElement = 4; else\r
798         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
799         if(msg.hwnd == hText)  currentElement = 5; else\r
800         if(msg.hwnd == hInput) currentElement = 6; else\r
801         for (i = 0; i < N_BUTTONS; i++) {\r
802             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
803         }\r
804 \r
805         // determine where to go to\r
806         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
807           do {\r
808             currentElement = (currentElement + direction) % 7;\r
809             switch(currentElement) {\r
810                 case 0:\r
811                   h = hwndMain; break; // passing this case always makes the loop exit\r
812                 case 1:\r
813                   h = buttonDesc[0].hwnd; break; // could be NULL\r
814                 case 2:\r
815                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
816                   h = e1; break;\r
817                 case 3:\r
818                   if(!EngineOutputIsUp()) continue;\r
819                   h = e2; break;\r
820                 case 4:\r
821                   if(!MoveHistoryIsUp()) continue;\r
822                   h = mh; break;\r
823 //              case 6: // input to eval graph does not seem to get here!\r
824 //                if(!EvalGraphIsUp()) continue;\r
825 //                h = evalGraphDialog; break;\r
826                 case 5:\r
827                   if(!appData.icsActive) continue;\r
828                   SAY("display");\r
829                   h = hText; break;\r
830                 case 6:\r
831                   if(!appData.icsActive) continue;\r
832                   SAY("input");\r
833                   h = hInput; break;\r
834             }\r
835           } while(h == 0);\r
836 \r
837           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
838           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
839           SetFocus(h);\r
840 \r
841           continue; // this message now has been processed\r
842         }\r
843       }\r
844 \r
845       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
846           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
847           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
848           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
849           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
850           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
851           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
852           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
853           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
854           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
855         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
856         for(i=0; i<MAX_CHAT; i++) \r
857             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
858                 done = 1; break;\r
859         }\r
860         if(done) continue; // [HGM] chat: end patch\r
861         TranslateMessage(&msg); /* Translates virtual key codes */\r
862         DispatchMessage(&msg);  /* Dispatches message to window */\r
863       }\r
864     }\r
865 \r
866 \r
867   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
868 }\r
869 \r
870 /*---------------------------------------------------------------------------*\\r
871  *\r
872  * Initialization functions\r
873  *\r
874 \*---------------------------------------------------------------------------*/\r
875 \r
876 void\r
877 SetUserLogo()\r
878 {   // update user logo if necessary\r
879     static char oldUserName[MSG_SIZ], *curName;\r
880 \r
881     if(appData.autoLogo) {\r
882           curName = UserName();\r
883           if(strcmp(curName, oldUserName)) {\r
884             snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
885                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
886                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
887           }\r
888     }\r
889 }\r
890 \r
891 BOOL\r
892 InitApplication(HINSTANCE hInstance)\r
893 {\r
894   WNDCLASS wc;\r
895 \r
896   /* Fill in window class structure with parameters that describe the */\r
897   /* main window. */\r
898 \r
899   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
900   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
901   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
902   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
903   wc.hInstance     = hInstance;         /* Owner of this class */\r
904   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
905   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
906   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
907   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
908   wc.lpszClassName = szAppName;                 /* Name to register as */\r
909 \r
910   /* Register the window class and return success/failure code. */\r
911   if (!RegisterClass(&wc)) return FALSE;\r
912 \r
913   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
914   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
915   wc.cbClsExtra    = 0;\r
916   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
917   wc.hInstance     = hInstance;\r
918   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
919   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
920   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
921   wc.lpszMenuName  = NULL;\r
922   wc.lpszClassName = szConsoleName;\r
923 \r
924   if (!RegisterClass(&wc)) return FALSE;\r
925   return TRUE;\r
926 }\r
927 \r
928 \r
929 /* Set by InitInstance, used by EnsureOnScreen */\r
930 int screenHeight, screenWidth;\r
931 \r
932 void\r
933 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
934 {\r
935 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
936   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
937   if (*x > screenWidth - 32) *x = 0;\r
938   if (*y > screenHeight - 32) *y = 0;\r
939   if (*x < minX) *x = minX;\r
940   if (*y < minY) *y = minY;\r
941 }\r
942 \r
943 BOOL\r
944 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
945 {\r
946   HWND hwnd; /* Main window handle. */\r
947   int ibs;\r
948   WINDOWPLACEMENT wp;\r
949   char *filepart;\r
950 \r
951   hInst = hInstance;    /* Store instance handle in our global variable */\r
952   programName = szAppName;\r
953 \r
954   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
955     *filepart = NULLCHAR;\r
956   } else {\r
957     GetCurrentDirectory(MSG_SIZ, installDir);\r
958   }\r
959   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
960   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
961   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
962   /* xboard, and older WinBoards, controlled the move sound with the\r
963      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
964      always turn the option on (so that the backend will call us),\r
965      then let the user turn the sound off by setting it to silence if\r
966      desired.  To accommodate old winboard.ini files saved by old\r
967      versions of WinBoard, we also turn off the sound if the option\r
968      was initially set to false. [HGM] taken out of InitAppData */\r
969   if (!appData.ringBellAfterMoves) {\r
970     sounds[(int)SoundMove].name = strdup("");\r
971     appData.ringBellAfterMoves = TRUE;\r
972   }\r
973   if (appData.debugMode) {\r
974     debugFP = fopen(appData.nameOfDebugFile, "w");\r
975     setbuf(debugFP, NULL);\r
976   }\r
977 \r
978   LoadLanguageFile(appData.language);\r
979 \r
980   InitBackEnd1();\r
981 \r
982 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
983 //  InitEngineUCI( installDir, &second );\r
984 \r
985   /* Create a main window for this application instance. */\r
986   hwnd = CreateWindow(szAppName, szTitle,\r
987                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
988                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
989                       NULL, NULL, hInstance, NULL);\r
990   hwndMain = hwnd;\r
991 \r
992   /* If window could not be created, return "failure" */\r
993   if (!hwnd) {\r
994     return (FALSE);\r
995   }\r
996 \r
997   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
998   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
999       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1000 \r
1001       if (first.programLogo == NULL && appData.debugMode) {\r
1002           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
1003       }\r
1004   } else if(appData.autoLogo) {\r
1005       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
1006         char buf[MSG_SIZ];\r
1007           snprintf(buf, MSG_SIZ, "%s/logo.bmp", appData.firstDirectory);\r
1008         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
1009       }\r
1010   }\r
1011 \r
1012   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
1013       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1014 \r
1015       if (second.programLogo == NULL && appData.debugMode) {\r
1016           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
1017       }\r
1018   } else if(appData.autoLogo) {\r
1019       char buf[MSG_SIZ];\r
1020       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
1021         snprintf(buf, MSG_SIZ, "logos\\%s.bmp", appData.icsHost);\r
1022         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1023       } else\r
1024       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
1025         snprintf(buf, MSG_SIZ, "%s\\logo.bmp", appData.secondDirectory);\r
1026         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
1027       }\r
1028   }\r
1029 \r
1030   SetUserLogo();\r
1031 \r
1032   iconWhite = LoadIcon(hInstance, "icon_white");\r
1033   iconBlack = LoadIcon(hInstance, "icon_black");\r
1034   iconCurrent = iconWhite;\r
1035   InitDrawingColors();\r
1036   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1037   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1038   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1039     /* Compute window size for each board size, and use the largest\r
1040        size that fits on this screen as the default. */\r
1041     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1042     if (boardSize == (BoardSize)-1 &&\r
1043         winH <= screenHeight\r
1044            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1045         && winW <= screenWidth) {\r
1046       boardSize = (BoardSize)ibs;\r
1047     }\r
1048   }\r
1049 \r
1050   InitDrawingSizes(boardSize, 0);\r
1051   TranslateMenus(1);\r
1052   InitMenuChecks();\r
1053   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1054 \r
1055   /* [AS] Load textures if specified */\r
1056   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1057   \r
1058   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1059       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1060       liteBackTextureMode = appData.liteBackTextureMode;\r
1061 \r
1062       if (liteBackTexture == NULL && appData.debugMode) {\r
1063           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1064       }\r
1065   }\r
1066   \r
1067   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1068       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1069       darkBackTextureMode = appData.darkBackTextureMode;\r
1070 \r
1071       if (darkBackTexture == NULL && appData.debugMode) {\r
1072           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1073       }\r
1074   }\r
1075 \r
1076   mysrandom( (unsigned) time(NULL) );\r
1077 \r
1078   /* [AS] Restore layout */\r
1079   if( wpMoveHistory.visible ) {\r
1080       MoveHistoryPopUp();\r
1081   }\r
1082 \r
1083   if( wpEvalGraph.visible ) {\r
1084       EvalGraphPopUp();\r
1085   }\r
1086 \r
1087   if( wpEngineOutput.visible ) {\r
1088       EngineOutputPopUp();\r
1089   }\r
1090 \r
1091   /* Make the window visible; update its client area; and return "success" */\r
1092   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1093   wp.length = sizeof(WINDOWPLACEMENT);\r
1094   wp.flags = 0;\r
1095   wp.showCmd = nCmdShow;\r
1096   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1097   wp.rcNormalPosition.left = wpMain.x;\r
1098   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1099   wp.rcNormalPosition.top = wpMain.y;\r
1100   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1101   SetWindowPlacement(hwndMain, &wp);\r
1102 \r
1103   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1104 \r
1105   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1106                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1107 \r
1108   if (hwndConsole) {\r
1109 #if AOT_CONSOLE\r
1110     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1111                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1112 #endif\r
1113     ShowWindow(hwndConsole, nCmdShow);\r
1114     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
1115       char buf[MSG_SIZ], *p = buf, *q;\r
1116         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
1117       do {\r
1118         q = strchr(p, ';');\r
1119         if(q) *q++ = 0;\r
1120         if(*p) ChatPopUp(p);\r
1121       } while(p=q);\r
1122     }\r
1123     SetActiveWindow(hwndConsole);\r
1124   }\r
1125   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1126   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1127 \r
1128   return TRUE;\r
1129 \r
1130 }\r
1131 \r
1132 VOID\r
1133 InitMenuChecks()\r
1134 {\r
1135   HMENU hmenu = GetMenu(hwndMain);\r
1136 \r
1137   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1138                         MF_BYCOMMAND|((appData.icsActive &&\r
1139                                        *appData.icsCommPort != NULLCHAR) ?\r
1140                                       MF_ENABLED : MF_GRAYED));\r
1141   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1142                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1143                                      MF_CHECKED : MF_UNCHECKED));\r
1144 }\r
1145 \r
1146 //---------------------------------------------------------------------------------------------------------\r
1147 \r
1148 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1149 #define XBOARD FALSE\r
1150 \r
1151 #define OPTCHAR "/"\r
1152 #define SEPCHAR "="\r
1153 \r
1154 #include "args.h"\r
1155 \r
1156 // front-end part of option handling\r
1157 \r
1158 VOID\r
1159 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1160 {\r
1161   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1162   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1163   DeleteDC(hdc);\r
1164   lf->lfWidth = 0;\r
1165   lf->lfEscapement = 0;\r
1166   lf->lfOrientation = 0;\r
1167   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1168   lf->lfItalic = mfp->italic;\r
1169   lf->lfUnderline = mfp->underline;\r
1170   lf->lfStrikeOut = mfp->strikeout;\r
1171   lf->lfCharSet = mfp->charset;\r
1172   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1173   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1174   lf->lfQuality = DEFAULT_QUALITY;\r
1175   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1176     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1177 }\r
1178 \r
1179 void\r
1180 CreateFontInMF(MyFont *mf)\r
1181\r
1182   LFfromMFP(&mf->lf, &mf->mfp);\r
1183   if (mf->hf) DeleteObject(mf->hf);\r
1184   mf->hf = CreateFontIndirect(&mf->lf);\r
1185 }\r
1186 \r
1187 // [HGM] This platform-dependent table provides the location for storing the color info\r
1188 void *\r
1189 colorVariable[] = {\r
1190   &whitePieceColor, \r
1191   &blackPieceColor, \r
1192   &lightSquareColor,\r
1193   &darkSquareColor, \r
1194   &highlightSquareColor,\r
1195   &premoveHighlightColor,\r
1196   NULL,\r
1197   &consoleBackgroundColor,\r
1198   &appData.fontForeColorWhite,\r
1199   &appData.fontBackColorWhite,\r
1200   &appData.fontForeColorBlack,\r
1201   &appData.fontBackColorBlack,\r
1202   &appData.evalHistColorWhite,\r
1203   &appData.evalHistColorBlack,\r
1204   &appData.highlightArrowColor,\r
1205 };\r
1206 \r
1207 /* Command line font name parser.  NULL name means do nothing.\r
1208    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1209    For backward compatibility, syntax without the colon is also\r
1210    accepted, but font names with digits in them won't work in that case.\r
1211 */\r
1212 VOID\r
1213 ParseFontName(char *name, MyFontParams *mfp)\r
1214 {\r
1215   char *p, *q;\r
1216   if (name == NULL) return;\r
1217   p = name;\r
1218   q = strchr(p, ':');\r
1219   if (q) {\r
1220     if (q - p >= sizeof(mfp->faceName))\r
1221       ExitArgError(_("Font name too long:"), name);\r
1222     memcpy(mfp->faceName, p, q - p);\r
1223     mfp->faceName[q - p] = NULLCHAR;\r
1224     p = q + 1;\r
1225   } else {\r
1226     q = mfp->faceName;\r
1227     while (*p && !isdigit(*p)) {\r
1228       *q++ = *p++;\r
1229       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1230         ExitArgError(_("Font name too long:"), name);\r
1231     }\r
1232     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1233     *q = NULLCHAR;\r
1234   }\r
1235   if (!*p) ExitArgError(_("Font point size missing:"), name);\r
1236   mfp->pointSize = (float) atof(p);\r
1237   mfp->bold = (strchr(p, 'b') != NULL);\r
1238   mfp->italic = (strchr(p, 'i') != NULL);\r
1239   mfp->underline = (strchr(p, 'u') != NULL);\r
1240   mfp->strikeout = (strchr(p, 's') != NULL);\r
1241   mfp->charset = DEFAULT_CHARSET;\r
1242   q = strchr(p, 'c');\r
1243   if (q)\r
1244     mfp->charset = (BYTE) atoi(q+1);\r
1245 }\r
1246 \r
1247 void\r
1248 ParseFont(char *name, int number)\r
1249 { // wrapper to shield back-end from 'font'\r
1250   ParseFontName(name, &font[boardSize][number]->mfp);\r
1251 }\r
1252 \r
1253 void\r
1254 SetFontDefaults()\r
1255 { // in WB  we have a 2D array of fonts; this initializes their description\r
1256   int i, j;\r
1257   /* Point font array elements to structures and\r
1258      parse default font names */\r
1259   for (i=0; i<NUM_FONTS; i++) {\r
1260     for (j=0; j<NUM_SIZES; j++) {\r
1261       font[j][i] = &fontRec[j][i];\r
1262       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1263     }\r
1264   }\r
1265 }\r
1266 \r
1267 void\r
1268 CreateFonts()\r
1269 { // here we create the actual fonts from the selected descriptions\r
1270   int i, j;\r
1271   for (i=0; i<NUM_FONTS; i++) {\r
1272     for (j=0; j<NUM_SIZES; j++) {\r
1273       CreateFontInMF(font[j][i]);\r
1274     }\r
1275   }\r
1276 }\r
1277 /* Color name parser.\r
1278    X version accepts X color names, but this one\r
1279    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1280 COLORREF\r
1281 ParseColorName(char *name)\r
1282 {\r
1283   int red, green, blue, count;\r
1284   char buf[MSG_SIZ];\r
1285 \r
1286   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1287   if (count != 3) {\r
1288     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1289       &red, &green, &blue);\r
1290   }\r
1291   if (count != 3) {\r
1292     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1293     DisplayError(buf, 0);\r
1294     return RGB(0, 0, 0);\r
1295   }\r
1296   return PALETTERGB(red, green, blue);\r
1297 }\r
1298 \r
1299 void\r
1300 ParseColor(int n, char *name)\r
1301 { // for WinBoard the color is an int, which needs to be derived from the string\r
1302   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1303 }\r
1304 \r
1305 void\r
1306 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1307 {\r
1308   char *e = argValue;\r
1309   int eff = 0;\r
1310 \r
1311   while (*e) {\r
1312     if (*e == 'b')      eff |= CFE_BOLD;\r
1313     else if (*e == 'i') eff |= CFE_ITALIC;\r
1314     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1315     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1316     else if (*e == '#' || isdigit(*e)) break;\r
1317     e++;\r
1318   }\r
1319   *effects = eff;\r
1320   *color   = ParseColorName(e);\r
1321 }\r
1322 \r
1323 void\r
1324 ParseTextAttribs(ColorClass cc, char *s)\r
1325 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1326     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1327     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1328 }\r
1329 \r
1330 void\r
1331 ParseBoardSize(void *addr, char *name)\r
1332 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1333   BoardSize bs = SizeTiny;\r
1334   while (sizeInfo[bs].name != NULL) {\r
1335     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1336         *(BoardSize *)addr = bs;\r
1337         return;\r
1338     }\r
1339     bs++;\r
1340   }\r
1341   ExitArgError(_("Unrecognized board size value"), name);\r
1342 }\r
1343 \r
1344 void\r
1345 LoadAllSounds()\r
1346 { // [HGM] import name from appData first\r
1347   ColorClass cc;\r
1348   SoundClass sc;\r
1349   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1350     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1351     textAttribs[cc].sound.data = NULL;\r
1352     MyLoadSound(&textAttribs[cc].sound);\r
1353   }\r
1354   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1355     textAttribs[cc].sound.name = strdup("");\r
1356     textAttribs[cc].sound.data = NULL;\r
1357   }\r
1358   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1359     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1360     sounds[sc].data = NULL;\r
1361     MyLoadSound(&sounds[sc]);\r
1362   }\r
1363 }\r
1364 \r
1365 void\r
1366 SetCommPortDefaults()\r
1367 {\r
1368    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1369   dcb.DCBlength = sizeof(DCB);\r
1370   dcb.BaudRate = 9600;\r
1371   dcb.fBinary = TRUE;\r
1372   dcb.fParity = FALSE;\r
1373   dcb.fOutxCtsFlow = FALSE;\r
1374   dcb.fOutxDsrFlow = FALSE;\r
1375   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1376   dcb.fDsrSensitivity = FALSE;\r
1377   dcb.fTXContinueOnXoff = TRUE;\r
1378   dcb.fOutX = FALSE;\r
1379   dcb.fInX = FALSE;\r
1380   dcb.fNull = FALSE;\r
1381   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1382   dcb.fAbortOnError = FALSE;\r
1383   dcb.ByteSize = 7;\r
1384   dcb.Parity = SPACEPARITY;\r
1385   dcb.StopBits = ONESTOPBIT;\r
1386 }\r
1387 \r
1388 // [HGM] args: these three cases taken out to stay in front-end\r
1389 void\r
1390 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1391 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1392         // while the curent board size determines the element. This system should be ported to XBoard.\r
1393         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1394         int bs;\r
1395         for (bs=0; bs<NUM_SIZES; bs++) {\r
1396           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1397           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1398           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1399             ad->argName, mfp->faceName, mfp->pointSize,\r
1400             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1401             mfp->bold ? "b" : "",\r
1402             mfp->italic ? "i" : "",\r
1403             mfp->underline ? "u" : "",\r
1404             mfp->strikeout ? "s" : "",\r
1405             (int)mfp->charset);\r
1406         }\r
1407       }\r
1408 \r
1409 void\r
1410 ExportSounds()\r
1411 { // [HGM] copy the names from the internal WB variables to appData\r
1412   ColorClass cc;\r
1413   SoundClass sc;\r
1414   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1415     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1416   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1417     (&appData.soundMove)[sc] = sounds[sc].name;\r
1418 }\r
1419 \r
1420 void\r
1421 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1422 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1423         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1424         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1425           (ta->effects & CFE_BOLD) ? "b" : "",\r
1426           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1427           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1428           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1429           (ta->effects) ? " " : "",\r
1430           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1431       }\r
1432 \r
1433 void\r
1434 SaveColor(FILE *f, ArgDescriptor *ad)\r
1435 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1436         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1437         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1438           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1439 }\r
1440 \r
1441 void\r
1442 SaveBoardSize(FILE *f, char *name, void *addr)\r
1443 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1444   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1445 }\r
1446 \r
1447 void\r
1448 ParseCommPortSettings(char *s)\r
1449 { // wrapper to keep dcb from back-end\r
1450   ParseCommSettings(s, &dcb);\r
1451 }\r
1452 \r
1453 void\r
1454 GetWindowCoords()\r
1455 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1456   GetActualPlacement(hwndMain, &wpMain);\r
1457   GetActualPlacement(hwndConsole, &wpConsole);\r
1458   GetActualPlacement(commentDialog, &wpComment);\r
1459   GetActualPlacement(editTagsDialog, &wpTags);\r
1460   GetActualPlacement(gameListDialog, &wpGameList);\r
1461   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1462   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1463   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1464 }\r
1465 \r
1466 void\r
1467 PrintCommPortSettings(FILE *f, char *name)\r
1468 { // wrapper to shield back-end from DCB\r
1469       PrintCommSettings(f, name, &dcb);\r
1470 }\r
1471 \r
1472 int\r
1473 MySearchPath(char *installDir, char *name, char *fullname)\r
1474 {\r
1475   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1476   if(name[0]== '%') {\r
1477     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1478     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1479       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1480       *strchr(buf, '%') = 0;\r
1481       strcat(fullname, getenv(buf));\r
1482       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1483     }\r
1484     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1485     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1486     return (int) strlen(fullname);\r
1487   }\r
1488   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1489 }\r
1490 \r
1491 int\r
1492 MyGetFullPathName(char *name, char *fullname)\r
1493 {\r
1494   char *dummy;\r
1495   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1496 }\r
1497 \r
1498 int\r
1499 MainWindowUp()\r
1500 { // [HGM] args: allows testing if main window is realized from back-end\r
1501   return hwndMain != NULL;\r
1502 }\r
1503 \r
1504 void\r
1505 PopUpStartupDialog()\r
1506 {\r
1507     FARPROC lpProc;\r
1508     \r
1509     LoadLanguageFile(appData.language);\r
1510     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1511     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1512     FreeProcInstance(lpProc);\r
1513 }\r
1514 \r
1515 /*---------------------------------------------------------------------------*\\r
1516  *\r
1517  * GDI board drawing routines\r
1518  *\r
1519 \*---------------------------------------------------------------------------*/\r
1520 \r
1521 /* [AS] Draw square using background texture */\r
1522 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1523 {\r
1524     XFORM   x;\r
1525 \r
1526     if( mode == 0 ) {\r
1527         return; /* Should never happen! */\r
1528     }\r
1529 \r
1530     SetGraphicsMode( dst, GM_ADVANCED );\r
1531 \r
1532     switch( mode ) {\r
1533     case 1:\r
1534         /* Identity */\r
1535         break;\r
1536     case 2:\r
1537         /* X reflection */\r
1538         x.eM11 = -1.0;\r
1539         x.eM12 = 0;\r
1540         x.eM21 = 0;\r
1541         x.eM22 = 1.0;\r
1542         x.eDx = (FLOAT) dw + dx - 1;\r
1543         x.eDy = 0;\r
1544         dx = 0;\r
1545         SetWorldTransform( dst, &x );\r
1546         break;\r
1547     case 3:\r
1548         /* Y reflection */\r
1549         x.eM11 = 1.0;\r
1550         x.eM12 = 0;\r
1551         x.eM21 = 0;\r
1552         x.eM22 = -1.0;\r
1553         x.eDx = 0;\r
1554         x.eDy = (FLOAT) dh + dy - 1;\r
1555         dy = 0;\r
1556         SetWorldTransform( dst, &x );\r
1557         break;\r
1558     case 4:\r
1559         /* X/Y flip */\r
1560         x.eM11 = 0;\r
1561         x.eM12 = 1.0;\r
1562         x.eM21 = 1.0;\r
1563         x.eM22 = 0;\r
1564         x.eDx = (FLOAT) dx;\r
1565         x.eDy = (FLOAT) dy;\r
1566         dx = 0;\r
1567         dy = 0;\r
1568         SetWorldTransform( dst, &x );\r
1569         break;\r
1570     }\r
1571 \r
1572     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1573 \r
1574     x.eM11 = 1.0;\r
1575     x.eM12 = 0;\r
1576     x.eM21 = 0;\r
1577     x.eM22 = 1.0;\r
1578     x.eDx = 0;\r
1579     x.eDy = 0;\r
1580     SetWorldTransform( dst, &x );\r
1581 \r
1582     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1583 }\r
1584 \r
1585 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1586 enum {\r
1587     PM_WP = (int) WhitePawn, \r
1588     PM_WN = (int) WhiteKnight, \r
1589     PM_WB = (int) WhiteBishop, \r
1590     PM_WR = (int) WhiteRook, \r
1591     PM_WQ = (int) WhiteQueen, \r
1592     PM_WF = (int) WhiteFerz, \r
1593     PM_WW = (int) WhiteWazir, \r
1594     PM_WE = (int) WhiteAlfil, \r
1595     PM_WM = (int) WhiteMan, \r
1596     PM_WO = (int) WhiteCannon, \r
1597     PM_WU = (int) WhiteUnicorn, \r
1598     PM_WH = (int) WhiteNightrider, \r
1599     PM_WA = (int) WhiteAngel, \r
1600     PM_WC = (int) WhiteMarshall, \r
1601     PM_WAB = (int) WhiteCardinal, \r
1602     PM_WD = (int) WhiteDragon, \r
1603     PM_WL = (int) WhiteLance, \r
1604     PM_WS = (int) WhiteCobra, \r
1605     PM_WV = (int) WhiteFalcon, \r
1606     PM_WSG = (int) WhiteSilver, \r
1607     PM_WG = (int) WhiteGrasshopper, \r
1608     PM_WK = (int) WhiteKing,\r
1609     PM_BP = (int) BlackPawn, \r
1610     PM_BN = (int) BlackKnight, \r
1611     PM_BB = (int) BlackBishop, \r
1612     PM_BR = (int) BlackRook, \r
1613     PM_BQ = (int) BlackQueen, \r
1614     PM_BF = (int) BlackFerz, \r
1615     PM_BW = (int) BlackWazir, \r
1616     PM_BE = (int) BlackAlfil, \r
1617     PM_BM = (int) BlackMan,\r
1618     PM_BO = (int) BlackCannon, \r
1619     PM_BU = (int) BlackUnicorn, \r
1620     PM_BH = (int) BlackNightrider, \r
1621     PM_BA = (int) BlackAngel, \r
1622     PM_BC = (int) BlackMarshall, \r
1623     PM_BG = (int) BlackGrasshopper, \r
1624     PM_BAB = (int) BlackCardinal,\r
1625     PM_BD = (int) BlackDragon,\r
1626     PM_BL = (int) BlackLance,\r
1627     PM_BS = (int) BlackCobra,\r
1628     PM_BV = (int) BlackFalcon,\r
1629     PM_BSG = (int) BlackSilver,\r
1630     PM_BK = (int) BlackKing\r
1631 };\r
1632 \r
1633 static HFONT hPieceFont = NULL;\r
1634 static HBITMAP hPieceMask[(int) EmptySquare];\r
1635 static HBITMAP hPieceFace[(int) EmptySquare];\r
1636 static int fontBitmapSquareSize = 0;\r
1637 static char pieceToFontChar[(int) EmptySquare] =\r
1638                               { 'p', 'n', 'b', 'r', 'q', \r
1639                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1640                       'k', 'o', 'm', 'v', 't', 'w', \r
1641                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1642                                                               'l' };\r
1643 \r
1644 extern BOOL SetCharTable( char *table, const char * map );\r
1645 /* [HGM] moved to backend.c */\r
1646 \r
1647 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1648 {\r
1649     HBRUSH hbrush;\r
1650     BYTE r1 = GetRValue( color );\r
1651     BYTE g1 = GetGValue( color );\r
1652     BYTE b1 = GetBValue( color );\r
1653     BYTE r2 = r1 / 2;\r
1654     BYTE g2 = g1 / 2;\r
1655     BYTE b2 = b1 / 2;\r
1656     RECT rc;\r
1657 \r
1658     /* Create a uniform background first */\r
1659     hbrush = CreateSolidBrush( color );\r
1660     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1661     FillRect( hdc, &rc, hbrush );\r
1662     DeleteObject( hbrush );\r
1663     \r
1664     if( mode == 1 ) {\r
1665         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1666         int steps = squareSize / 2;\r
1667         int i;\r
1668 \r
1669         for( i=0; i<steps; i++ ) {\r
1670             BYTE r = r1 - (r1-r2) * i / steps;\r
1671             BYTE g = g1 - (g1-g2) * i / steps;\r
1672             BYTE b = b1 - (b1-b2) * i / steps;\r
1673 \r
1674             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1675             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1676             FillRect( hdc, &rc, hbrush );\r
1677             DeleteObject(hbrush);\r
1678         }\r
1679     }\r
1680     else if( mode == 2 ) {\r
1681         /* Diagonal gradient, good more or less for every piece */\r
1682         POINT triangle[3];\r
1683         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1684         HBRUSH hbrush_old;\r
1685         int steps = squareSize;\r
1686         int i;\r
1687 \r
1688         triangle[0].x = squareSize - steps;\r
1689         triangle[0].y = squareSize;\r
1690         triangle[1].x = squareSize;\r
1691         triangle[1].y = squareSize;\r
1692         triangle[2].x = squareSize;\r
1693         triangle[2].y = squareSize - steps;\r
1694 \r
1695         for( i=0; i<steps; i++ ) {\r
1696             BYTE r = r1 - (r1-r2) * i / steps;\r
1697             BYTE g = g1 - (g1-g2) * i / steps;\r
1698             BYTE b = b1 - (b1-b2) * i / steps;\r
1699 \r
1700             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1701             hbrush_old = SelectObject( hdc, hbrush );\r
1702             Polygon( hdc, triangle, 3 );\r
1703             SelectObject( hdc, hbrush_old );\r
1704             DeleteObject(hbrush);\r
1705             triangle[0].x++;\r
1706             triangle[2].y++;\r
1707         }\r
1708 \r
1709         SelectObject( hdc, hpen );\r
1710     }\r
1711 }\r
1712 \r
1713 /*\r
1714     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1715     seems to work ok. The main problem here is to find the "inside" of a chess\r
1716     piece: follow the steps as explained below.\r
1717 */\r
1718 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1719 {\r
1720     HBITMAP hbm;\r
1721     HBITMAP hbm_old;\r
1722     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1723     RECT rc;\r
1724     SIZE sz;\r
1725     POINT pt;\r
1726     int backColor = whitePieceColor; \r
1727     int foreColor = blackPieceColor;\r
1728     \r
1729     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1730         backColor = appData.fontBackColorWhite;\r
1731         foreColor = appData.fontForeColorWhite;\r
1732     }\r
1733     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1734         backColor = appData.fontBackColorBlack;\r
1735         foreColor = appData.fontForeColorBlack;\r
1736     }\r
1737 \r
1738     /* Mask */\r
1739     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1740 \r
1741     hbm_old = SelectObject( hdc, hbm );\r
1742 \r
1743     rc.left = 0;\r
1744     rc.top = 0;\r
1745     rc.right = squareSize;\r
1746     rc.bottom = squareSize;\r
1747 \r
1748     /* Step 1: background is now black */\r
1749     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1750 \r
1751     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1752 \r
1753     pt.x = (squareSize - sz.cx) / 2;\r
1754     pt.y = (squareSize - sz.cy) / 2;\r
1755 \r
1756     SetBkMode( hdc, TRANSPARENT );\r
1757     SetTextColor( hdc, chroma );\r
1758     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1759     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1760 \r
1761     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1762     /* Step 3: the area outside the piece is filled with white */\r
1763 //    FloodFill( hdc, 0, 0, chroma );\r
1764     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1765     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1766     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1767     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1768     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1769     /* \r
1770         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1771         but if the start point is not inside the piece we're lost!\r
1772         There should be a better way to do this... if we could create a region or path\r
1773         from the fill operation we would be fine for example.\r
1774     */\r
1775 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1776     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1777 \r
1778     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1779         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1780         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1781 \r
1782         SelectObject( dc2, bm2 );\r
1783         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1784         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1785         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1786         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1787         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1788 \r
1789         DeleteDC( dc2 );\r
1790         DeleteObject( bm2 );\r
1791     }\r
1792 \r
1793     SetTextColor( hdc, 0 );\r
1794     /* \r
1795         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1796         draw the piece again in black for safety.\r
1797     */\r
1798     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1799 \r
1800     SelectObject( hdc, hbm_old );\r
1801 \r
1802     if( hPieceMask[index] != NULL ) {\r
1803         DeleteObject( hPieceMask[index] );\r
1804     }\r
1805 \r
1806     hPieceMask[index] = hbm;\r
1807 \r
1808     /* Face */\r
1809     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1810 \r
1811     SelectObject( hdc, hbm );\r
1812 \r
1813     {\r
1814         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1815         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1816         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1817 \r
1818         SelectObject( dc1, hPieceMask[index] );\r
1819         SelectObject( dc2, bm2 );\r
1820         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1821         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1822         \r
1823         /* \r
1824             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1825             the piece background and deletes (makes transparent) the rest.\r
1826             Thanks to that mask, we are free to paint the background with the greates\r
1827             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1828             We use this, to make gradients and give the pieces a "roundish" look.\r
1829         */\r
1830         SetPieceBackground( hdc, backColor, 2 );\r
1831         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1832 \r
1833         DeleteDC( dc2 );\r
1834         DeleteDC( dc1 );\r
1835         DeleteObject( bm2 );\r
1836     }\r
1837 \r
1838     SetTextColor( hdc, foreColor );\r
1839     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1840 \r
1841     SelectObject( hdc, hbm_old );\r
1842 \r
1843     if( hPieceFace[index] != NULL ) {\r
1844         DeleteObject( hPieceFace[index] );\r
1845     }\r
1846 \r
1847     hPieceFace[index] = hbm;\r
1848 }\r
1849 \r
1850 static int TranslatePieceToFontPiece( int piece )\r
1851 {\r
1852     switch( piece ) {\r
1853     case BlackPawn:\r
1854         return PM_BP;\r
1855     case BlackKnight:\r
1856         return PM_BN;\r
1857     case BlackBishop:\r
1858         return PM_BB;\r
1859     case BlackRook:\r
1860         return PM_BR;\r
1861     case BlackQueen:\r
1862         return PM_BQ;\r
1863     case BlackKing:\r
1864         return PM_BK;\r
1865     case WhitePawn:\r
1866         return PM_WP;\r
1867     case WhiteKnight:\r
1868         return PM_WN;\r
1869     case WhiteBishop:\r
1870         return PM_WB;\r
1871     case WhiteRook:\r
1872         return PM_WR;\r
1873     case WhiteQueen:\r
1874         return PM_WQ;\r
1875     case WhiteKing:\r
1876         return PM_WK;\r
1877 \r
1878     case BlackAngel:\r
1879         return PM_BA;\r
1880     case BlackMarshall:\r
1881         return PM_BC;\r
1882     case BlackFerz:\r
1883         return PM_BF;\r
1884     case BlackNightrider:\r
1885         return PM_BH;\r
1886     case BlackAlfil:\r
1887         return PM_BE;\r
1888     case BlackWazir:\r
1889         return PM_BW;\r
1890     case BlackUnicorn:\r
1891         return PM_BU;\r
1892     case BlackCannon:\r
1893         return PM_BO;\r
1894     case BlackGrasshopper:\r
1895         return PM_BG;\r
1896     case BlackMan:\r
1897         return PM_BM;\r
1898     case BlackSilver:\r
1899         return PM_BSG;\r
1900     case BlackLance:\r
1901         return PM_BL;\r
1902     case BlackFalcon:\r
1903         return PM_BV;\r
1904     case BlackCobra:\r
1905         return PM_BS;\r
1906     case BlackCardinal:\r
1907         return PM_BAB;\r
1908     case BlackDragon:\r
1909         return PM_BD;\r
1910 \r
1911     case WhiteAngel:\r
1912         return PM_WA;\r
1913     case WhiteMarshall:\r
1914         return PM_WC;\r
1915     case WhiteFerz:\r
1916         return PM_WF;\r
1917     case WhiteNightrider:\r
1918         return PM_WH;\r
1919     case WhiteAlfil:\r
1920         return PM_WE;\r
1921     case WhiteWazir:\r
1922         return PM_WW;\r
1923     case WhiteUnicorn:\r
1924         return PM_WU;\r
1925     case WhiteCannon:\r
1926         return PM_WO;\r
1927     case WhiteGrasshopper:\r
1928         return PM_WG;\r
1929     case WhiteMan:\r
1930         return PM_WM;\r
1931     case WhiteSilver:\r
1932         return PM_WSG;\r
1933     case WhiteLance:\r
1934         return PM_WL;\r
1935     case WhiteFalcon:\r
1936         return PM_WV;\r
1937     case WhiteCobra:\r
1938         return PM_WS;\r
1939     case WhiteCardinal:\r
1940         return PM_WAB;\r
1941     case WhiteDragon:\r
1942         return PM_WD;\r
1943     }\r
1944 \r
1945     return 0;\r
1946 }\r
1947 \r
1948 void CreatePiecesFromFont()\r
1949 {\r
1950     LOGFONT lf;\r
1951     HDC hdc_window = NULL;\r
1952     HDC hdc = NULL;\r
1953     HFONT hfont_old;\r
1954     int fontHeight;\r
1955     int i;\r
1956 \r
1957     if( fontBitmapSquareSize < 0 ) {\r
1958         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
1959         return;\r
1960     }\r
1961 \r
1962     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
1963         fontBitmapSquareSize = -1;\r
1964         return;\r
1965     }\r
1966 \r
1967     if( fontBitmapSquareSize != squareSize ) {\r
1968         hdc_window = GetDC( hwndMain );\r
1969         hdc = CreateCompatibleDC( hdc_window );\r
1970 \r
1971         if( hPieceFont != NULL ) {\r
1972             DeleteObject( hPieceFont );\r
1973         }\r
1974         else {\r
1975             for( i=0; i<=(int)BlackKing; i++ ) {\r
1976                 hPieceMask[i] = NULL;\r
1977                 hPieceFace[i] = NULL;\r
1978             }\r
1979         }\r
1980 \r
1981         fontHeight = 75;\r
1982 \r
1983         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
1984             fontHeight = appData.fontPieceSize;\r
1985         }\r
1986 \r
1987         fontHeight = (fontHeight * squareSize) / 100;\r
1988 \r
1989         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
1990         lf.lfWidth = 0;\r
1991         lf.lfEscapement = 0;\r
1992         lf.lfOrientation = 0;\r
1993         lf.lfWeight = FW_NORMAL;\r
1994         lf.lfItalic = 0;\r
1995         lf.lfUnderline = 0;\r
1996         lf.lfStrikeOut = 0;\r
1997         lf.lfCharSet = DEFAULT_CHARSET;\r
1998         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1999         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2000         lf.lfQuality = PROOF_QUALITY;\r
2001         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2002         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2003         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2004 \r
2005         hPieceFont = CreateFontIndirect( &lf );\r
2006 \r
2007         if( hPieceFont == NULL ) {\r
2008             fontBitmapSquareSize = -2;\r
2009         }\r
2010         else {\r
2011             /* Setup font-to-piece character table */\r
2012             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2013                 /* No (or wrong) global settings, try to detect the font */\r
2014                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2015                     /* Alpha */\r
2016                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2017                 }\r
2018                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2019                     /* DiagramTT* family */\r
2020                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2021                 }\r
2022                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2023                     /* Fairy symbols */\r
2024                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2025                 }\r
2026                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2027                     /* Good Companion (Some characters get warped as literal :-( */\r
2028                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2029                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2030                     SetCharTable(pieceToFontChar, s);\r
2031                 }\r
2032                 else {\r
2033                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2034                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2035                 }\r
2036             }\r
2037 \r
2038             /* Create bitmaps */\r
2039             hfont_old = SelectObject( hdc, hPieceFont );\r
2040             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2041                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2042                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2043 \r
2044             SelectObject( hdc, hfont_old );\r
2045 \r
2046             fontBitmapSquareSize = squareSize;\r
2047         }\r
2048     }\r
2049 \r
2050     if( hdc != NULL ) {\r
2051         DeleteDC( hdc );\r
2052     }\r
2053 \r
2054     if( hdc_window != NULL ) {\r
2055         ReleaseDC( hwndMain, hdc_window );\r
2056     }\r
2057 }\r
2058 \r
2059 HBITMAP\r
2060 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2061 {\r
2062   char name[128];\r
2063 \r
2064     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2065   if (gameInfo.event &&\r
2066       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2067       strcmp(name, "k80s") == 0) {\r
2068     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2069   }\r
2070   return LoadBitmap(hinst, name);\r
2071 }\r
2072 \r
2073 \r
2074 /* Insert a color into the program's logical palette\r
2075    structure.  This code assumes the given color is\r
2076    the result of the RGB or PALETTERGB macro, and it\r
2077    knows how those macros work (which is documented).\r
2078 */\r
2079 VOID\r
2080 InsertInPalette(COLORREF color)\r
2081 {\r
2082   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2083 \r
2084   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2085     DisplayFatalError(_("Too many colors"), 0, 1);\r
2086     pLogPal->palNumEntries--;\r
2087     return;\r
2088   }\r
2089 \r
2090   pe->peFlags = (char) 0;\r
2091   pe->peRed = (char) (0xFF & color);\r
2092   pe->peGreen = (char) (0xFF & (color >> 8));\r
2093   pe->peBlue = (char) (0xFF & (color >> 16));\r
2094   return;\r
2095 }\r
2096 \r
2097 \r
2098 VOID\r
2099 InitDrawingColors()\r
2100 {\r
2101   if (pLogPal == NULL) {\r
2102     /* Allocate enough memory for a logical palette with\r
2103      * PALETTESIZE entries and set the size and version fields\r
2104      * of the logical palette structure.\r
2105      */\r
2106     pLogPal = (NPLOGPALETTE)\r
2107       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2108                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2109     pLogPal->palVersion    = 0x300;\r
2110   }\r
2111   pLogPal->palNumEntries = 0;\r
2112 \r
2113   InsertInPalette(lightSquareColor);\r
2114   InsertInPalette(darkSquareColor);\r
2115   InsertInPalette(whitePieceColor);\r
2116   InsertInPalette(blackPieceColor);\r
2117   InsertInPalette(highlightSquareColor);\r
2118   InsertInPalette(premoveHighlightColor);\r
2119 \r
2120   /*  create a logical color palette according the information\r
2121    *  in the LOGPALETTE structure.\r
2122    */\r
2123   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2124 \r
2125   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2126   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2127   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2128   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2129   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2130   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2131   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2132   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2133   /* [AS] Force rendering of the font-based pieces */\r
2134   if( fontBitmapSquareSize > 0 ) {\r
2135     fontBitmapSquareSize = 0;\r
2136   }\r
2137 }\r
2138 \r
2139 \r
2140 int\r
2141 BoardWidth(int boardSize, int n)\r
2142 { /* [HGM] argument n added to allow different width and height */\r
2143   int lineGap = sizeInfo[boardSize].lineGap;\r
2144 \r
2145   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2146       lineGap = appData.overrideLineGap;\r
2147   }\r
2148 \r
2149   return (n + 1) * lineGap +\r
2150           n * sizeInfo[boardSize].squareSize;\r
2151 }\r
2152 \r
2153 /* Respond to board resize by dragging edge */\r
2154 VOID\r
2155 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2156 {\r
2157   BoardSize newSize = NUM_SIZES - 1;\r
2158   static int recurse = 0;\r
2159   if (IsIconic(hwndMain)) return;\r
2160   if (recurse > 0) return;\r
2161   recurse++;\r
2162   while (newSize > 0) {\r
2163         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2164         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2165            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2166     newSize--;\r
2167   } \r
2168   boardSize = newSize;\r
2169   InitDrawingSizes(boardSize, flags);\r
2170   recurse--;\r
2171 }\r
2172 \r
2173 \r
2174 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2175 \r
2176 VOID\r
2177 InitDrawingSizes(BoardSize boardSize, int flags)\r
2178 {\r
2179   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2180   ChessSquare piece;\r
2181   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2182   HDC hdc;\r
2183   SIZE clockSize, messageSize;\r
2184   HFONT oldFont;\r
2185   char buf[MSG_SIZ];\r
2186   char *str;\r
2187   HMENU hmenu = GetMenu(hwndMain);\r
2188   RECT crect, wrect, oldRect;\r
2189   int offby;\r
2190   LOGBRUSH logbrush;\r
2191 \r
2192   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2193   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2194 \r
2195   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2196   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2197 \r
2198   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2199   oldRect.top = wpMain.y;\r
2200   oldRect.right = wpMain.x + wpMain.width;\r
2201   oldRect.bottom = wpMain.y + wpMain.height;\r
2202 \r
2203   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2204   smallLayout = sizeInfo[boardSize].smallLayout;\r
2205   squareSize = sizeInfo[boardSize].squareSize;\r
2206   lineGap = sizeInfo[boardSize].lineGap;\r
2207   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2208 \r
2209   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2210       lineGap = appData.overrideLineGap;\r
2211   }\r
2212 \r
2213   if (tinyLayout != oldTinyLayout) {\r
2214     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
2215     if (tinyLayout) {\r
2216       style &= ~WS_SYSMENU;\r
2217       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2218                  "&Minimize\tCtrl+F4");\r
2219     } else {\r
2220       style |= WS_SYSMENU;\r
2221       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2222     }\r
2223     SetWindowLong(hwndMain, GWL_STYLE, style);\r
2224 \r
2225     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2226       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2227         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2228     }\r
2229     DrawMenuBar(hwndMain);\r
2230   }\r
2231 \r
2232   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2233   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2234 \r
2235   /* Get text area sizes */\r
2236   hdc = GetDC(hwndMain);\r
2237   if (appData.clockMode) {\r
2238     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2239   } else {\r
2240     snprintf(buf, MSG_SIZ, _("White"));\r
2241   }\r
2242   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2243   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2244   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2245   str = _("We only care about the height here");\r
2246   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2247   SelectObject(hdc, oldFont);\r
2248   ReleaseDC(hwndMain, hdc);\r
2249 \r
2250   /* Compute where everything goes */\r
2251   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2252         /* [HGM] logo: if either logo is on, reserve space for it */\r
2253         logoHeight =  2*clockSize.cy;\r
2254         leftLogoRect.left   = OUTER_MARGIN;\r
2255         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2256         leftLogoRect.top    = OUTER_MARGIN;\r
2257         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2258 \r
2259         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2260         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2261         rightLogoRect.top    = OUTER_MARGIN;\r
2262         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2263 \r
2264 \r
2265     whiteRect.left = leftLogoRect.right;\r
2266     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2267     whiteRect.top = OUTER_MARGIN;\r
2268     whiteRect.bottom = whiteRect.top + logoHeight;\r
2269 \r
2270     blackRect.right = rightLogoRect.left;\r
2271     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2272     blackRect.top = whiteRect.top;\r
2273     blackRect.bottom = whiteRect.bottom;\r
2274   } else {\r
2275     whiteRect.left = OUTER_MARGIN;\r
2276     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2277     whiteRect.top = OUTER_MARGIN;\r
2278     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2279 \r
2280     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2281     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2282     blackRect.top = whiteRect.top;\r
2283     blackRect.bottom = whiteRect.bottom;\r
2284 \r
2285     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2286   }\r
2287 \r
2288   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2289   if (appData.showButtonBar) {\r
2290     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2291       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2292   } else {\r
2293     messageRect.right = OUTER_MARGIN + boardWidth;\r
2294   }\r
2295   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2296   messageRect.bottom = messageRect.top + messageSize.cy;\r
2297 \r
2298   boardRect.left = OUTER_MARGIN;\r
2299   boardRect.right = boardRect.left + boardWidth;\r
2300   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2301   boardRect.bottom = boardRect.top + boardHeight;\r
2302 \r
2303   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2304   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2305   oldBoardSize = boardSize;\r
2306   oldTinyLayout = tinyLayout;\r
2307   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2308   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2309     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2310   winW *= 1 + twoBoards;\r
2311   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2312   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2313   wpMain.height = winH; //       without disturbing window attachments\r
2314   GetWindowRect(hwndMain, &wrect);\r
2315   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2316                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2317 \r
2318   // [HGM] placement: let attached windows follow size change.\r
2319   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2320   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2321   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2322   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2323   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2324 \r
2325   /* compensate if menu bar wrapped */\r
2326   GetClientRect(hwndMain, &crect);\r
2327   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2328   wpMain.height += offby;\r
2329   switch (flags) {\r
2330   case WMSZ_TOPLEFT:\r
2331     SetWindowPos(hwndMain, NULL, \r
2332                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2333                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2334     break;\r
2335 \r
2336   case WMSZ_TOPRIGHT:\r
2337   case WMSZ_TOP:\r
2338     SetWindowPos(hwndMain, NULL, \r
2339                  wrect.left, wrect.bottom - wpMain.height, \r
2340                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2341     break;\r
2342 \r
2343   case WMSZ_BOTTOMLEFT:\r
2344   case WMSZ_LEFT:\r
2345     SetWindowPos(hwndMain, NULL, \r
2346                  wrect.right - wpMain.width, wrect.top, \r
2347                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2348     break;\r
2349 \r
2350   case WMSZ_BOTTOMRIGHT:\r
2351   case WMSZ_BOTTOM:\r
2352   case WMSZ_RIGHT:\r
2353   default:\r
2354     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2355                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2356     break;\r
2357   }\r
2358 \r
2359   hwndPause = NULL;\r
2360   for (i = 0; i < N_BUTTONS; i++) {\r
2361     if (buttonDesc[i].hwnd != NULL) {\r
2362       DestroyWindow(buttonDesc[i].hwnd);\r
2363       buttonDesc[i].hwnd = NULL;\r
2364     }\r
2365     if (appData.showButtonBar) {\r
2366       buttonDesc[i].hwnd =\r
2367         CreateWindow("BUTTON", buttonDesc[i].label,\r
2368                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2369                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2370                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2371                      (HMENU) buttonDesc[i].id,\r
2372                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
2373       if (tinyLayout) {\r
2374         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2375                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2376                     MAKELPARAM(FALSE, 0));\r
2377       }\r
2378       if (buttonDesc[i].id == IDM_Pause)\r
2379         hwndPause = buttonDesc[i].hwnd;\r
2380       buttonDesc[i].wndproc = (WNDPROC)\r
2381         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
2382     }\r
2383   }\r
2384   if (gridPen != NULL) DeleteObject(gridPen);\r
2385   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2386   if (premovePen != NULL) DeleteObject(premovePen);\r
2387   if (lineGap != 0) {\r
2388     logbrush.lbStyle = BS_SOLID;\r
2389     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2390     gridPen =\r
2391       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2392                    lineGap, &logbrush, 0, NULL);\r
2393     logbrush.lbColor = highlightSquareColor;\r
2394     highlightPen =\r
2395       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2396                    lineGap, &logbrush, 0, NULL);\r
2397 \r
2398     logbrush.lbColor = premoveHighlightColor; \r
2399     premovePen =\r
2400       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2401                    lineGap, &logbrush, 0, NULL);\r
2402 \r
2403     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2404     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2405       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2406       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2407         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2408       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2409         BOARD_WIDTH * (squareSize + lineGap);\r
2410       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2411     }\r
2412     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2413       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2414       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2415         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2416         lineGap / 2 + (i * (squareSize + lineGap));\r
2417       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2418         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2419       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2420     }\r
2421   }\r
2422 \r
2423   /* [HGM] Licensing requirement */\r
2424 #ifdef GOTHIC\r
2425   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2426 #endif\r
2427 #ifdef FALCON\r
2428   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2429 #endif\r
2430   GothicPopUp( "", VariantNormal);\r
2431 \r
2432 \r
2433 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2434 \r
2435   /* Load piece bitmaps for this board size */\r
2436   for (i=0; i<=2; i++) {\r
2437     for (piece = WhitePawn;\r
2438          (int) piece < (int) BlackPawn;\r
2439          piece = (ChessSquare) ((int) piece + 1)) {\r
2440       if (pieceBitmap[i][piece] != NULL)\r
2441         DeleteObject(pieceBitmap[i][piece]);\r
2442     }\r
2443   }\r
2444 \r
2445   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2446   // Orthodox Chess pieces\r
2447   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2448   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2449   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2450   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2451   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2452   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2453   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2454   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2455   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2456   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2457   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2458   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2459   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2460   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2461   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2462   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2463     // in Shogi, Hijack the unused Queen for Lance\r
2464     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2465     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2466     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2467   } else {\r
2468     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2469     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2470     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2471   }\r
2472 \r
2473   if(squareSize <= 72 && squareSize >= 33) { \r
2474     /* A & C are available in most sizes now */\r
2475     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2476       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2477       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2478       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2479       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2480       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2481       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2482       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2483       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2484       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2485       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2486       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2487       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2488     } else { // Smirf-like\r
2489       if(gameInfo.variant == VariantSChess) {\r
2490         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2491         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2492         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2493       } else {\r
2494         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2495         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2496         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2497       }\r
2498     }\r
2499     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2500       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2501       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2502       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2503     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2504       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2505       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2506       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2507     } else { // WinBoard standard\r
2508       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2509       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2510       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2511     }\r
2512   }\r
2513 \r
2514 \r
2515   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2516     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2517     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2518     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2519     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2520     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2521     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2522     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2523     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2524     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2525     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2526     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2527     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2528     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2529     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2530     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2531     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2532     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2533     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2534     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2535     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2536     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2537     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2538     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2539     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2540     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2541     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2542     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2543     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2544     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2545     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2546 \r
2547     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2548       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2549       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2550       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2551       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2552       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2553       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2554       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2555       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2556       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2557       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2558       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2559       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2560     } else {\r
2561       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2562       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2563       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2564       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2565       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2566       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2567       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2568       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2569       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2570       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2571       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2572       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2573     }\r
2574 \r
2575   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2576     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2577     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2578     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2579     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2580     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2581     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2582     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2583     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2584     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2585     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2586     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2587     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2588     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2589     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2590   }\r
2591 \r
2592 \r
2593   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2594   /* special Shogi support in this size */\r
2595   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2596       for (piece = WhitePawn;\r
2597            (int) piece < (int) BlackPawn;\r
2598            piece = (ChessSquare) ((int) piece + 1)) {\r
2599         if (pieceBitmap[i][piece] != NULL)\r
2600           DeleteObject(pieceBitmap[i][piece]);\r
2601       }\r
2602     }\r
2603   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2604   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2605   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2606   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2607   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2608   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2609   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2610   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2611   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2612   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2613   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2614   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2615   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2616   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2617   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2618   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2619   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2620   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2621   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2622   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2623   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2624   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2625   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2626   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2627   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2628   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2629   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2630   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2631   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2632   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2633   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2634   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2635   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2636   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2637   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2638   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2639   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2640   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2641   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2642   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2643   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2644   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2645   minorSize = 0;\r
2646   }\r
2647 }\r
2648 \r
2649 HBITMAP\r
2650 PieceBitmap(ChessSquare p, int kind)\r
2651 {\r
2652   if ((int) p >= (int) BlackPawn)\r
2653     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2654 \r
2655   return pieceBitmap[kind][(int) p];\r
2656 }\r
2657 \r
2658 /***************************************************************/\r
2659 \r
2660 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2661 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2662 /*\r
2663 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2664 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2665 */\r
2666 \r
2667 VOID\r
2668 SquareToPos(int row, int column, int * x, int * y)\r
2669 {\r
2670   if (flipView) {\r
2671     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2672     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2673   } else {\r
2674     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2675     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2676   }\r
2677 }\r
2678 \r
2679 VOID\r
2680 DrawCoordsOnDC(HDC hdc)\r
2681 {\r
2682   static char files[24] = {'0', '1','2','3','4','5','6','7','8','9','0','1','1','0','9','8','7','6','5','4','3','2','1','0'};\r
2683   static char ranks[24] = {'l', 'k','j','i','h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h','i','j','k','l'};\r
2684   char str[2] = { NULLCHAR, NULLCHAR };\r
2685   int oldMode, oldAlign, x, y, start, i;\r
2686   HFONT oldFont;\r
2687   HBRUSH oldBrush;\r
2688 \r
2689   if (!appData.showCoords)\r
2690     return;\r
2691 \r
2692   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
2693 \r
2694   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2695   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2696   oldAlign = GetTextAlign(hdc);\r
2697   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2698 \r
2699   y = boardRect.top + lineGap;\r
2700   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2701 \r
2702   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2703   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2704     str[0] = files[start + i];\r
2705     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2706     y += squareSize + lineGap;\r
2707   }\r
2708 \r
2709   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
2710 \r
2711   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2712   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2713     str[0] = ranks[start + i];\r
2714     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2715     x += squareSize + lineGap;\r
2716   }    \r
2717 \r
2718   SelectObject(hdc, oldBrush);\r
2719   SetBkMode(hdc, oldMode);\r
2720   SetTextAlign(hdc, oldAlign);\r
2721   SelectObject(hdc, oldFont);\r
2722 }\r
2723 \r
2724 VOID\r
2725 DrawGridOnDC(HDC hdc)\r
2726 {\r
2727   HPEN oldPen;\r
2728  \r
2729   if (lineGap != 0) {\r
2730     oldPen = SelectObject(hdc, gridPen);\r
2731     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2732     SelectObject(hdc, oldPen);\r
2733   }\r
2734 }\r
2735 \r
2736 #define HIGHLIGHT_PEN 0\r
2737 #define PREMOVE_PEN   1\r
2738 \r
2739 VOID\r
2740 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2741 {\r
2742   int x1, y1;\r
2743   HPEN oldPen, hPen;\r
2744   if (lineGap == 0) return;\r
2745   if (flipView) {\r
2746     x1 = boardRect.left +\r
2747       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2748     y1 = boardRect.top +\r
2749       lineGap/2 + y * (squareSize + lineGap);\r
2750   } else {\r
2751     x1 = boardRect.left +\r
2752       lineGap/2 + x * (squareSize + lineGap);\r
2753     y1 = boardRect.top +\r
2754       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2755   }\r
2756   hPen = pen ? premovePen : highlightPen;\r
2757   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2758   MoveToEx(hdc, x1, y1, NULL);\r
2759   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2760   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2761   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2762   LineTo(hdc, x1, y1);\r
2763   SelectObject(hdc, oldPen);\r
2764 }\r
2765 \r
2766 VOID\r
2767 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2768 {\r
2769   int i;\r
2770   for (i=0; i<2; i++) {\r
2771     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2772       DrawHighlightOnDC(hdc, TRUE,\r
2773                         h->sq[i].x, h->sq[i].y,\r
2774                         pen);\r
2775   }\r
2776 }\r
2777 \r
2778 /* Note: sqcolor is used only in monoMode */\r
2779 /* Note that this code is largely duplicated in woptions.c,\r
2780    function DrawSampleSquare, so that needs to be updated too */\r
2781 VOID\r
2782 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2783 {\r
2784   HBITMAP oldBitmap;\r
2785   HBRUSH oldBrush;\r
2786   int tmpSize;\r
2787 \r
2788   if (appData.blindfold) return;\r
2789 \r
2790   /* [AS] Use font-based pieces if needed */\r
2791   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2792     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2793     CreatePiecesFromFont();\r
2794 \r
2795     if( fontBitmapSquareSize == squareSize ) {\r
2796         int index = TranslatePieceToFontPiece(piece);\r
2797 \r
2798         SelectObject( tmphdc, hPieceMask[ index ] );\r
2799 \r
2800       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2801         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2802       else\r
2803         BitBlt( hdc,\r
2804             x, y,\r
2805             squareSize, squareSize,\r
2806             tmphdc,\r
2807             0, 0,\r
2808             SRCAND );\r
2809 \r
2810         SelectObject( tmphdc, hPieceFace[ index ] );\r
2811 \r
2812       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2813         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2814       else\r
2815         BitBlt( hdc,\r
2816             x, y,\r
2817             squareSize, squareSize,\r
2818             tmphdc,\r
2819             0, 0,\r
2820             SRCPAINT );\r
2821 \r
2822         return;\r
2823     }\r
2824   }\r
2825 \r
2826   if (appData.monoMode) {\r
2827     SelectObject(tmphdc, PieceBitmap(piece, \r
2828       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2829     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2830            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2831   } else {\r
2832     tmpSize = squareSize;\r
2833     if(minorSize &&\r
2834         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2835          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2836       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2837       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2838       x += (squareSize - minorSize)>>1;\r
2839       y += squareSize - minorSize - 2;\r
2840       tmpSize = minorSize;\r
2841     }\r
2842     if (color || appData.allWhite ) {\r
2843       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2844       if( color )\r
2845               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2846       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2847       if(appData.upsideDown && color==flipView)\r
2848         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2849       else\r
2850         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2851       /* Use black for outline of white pieces */\r
2852       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2853       if(appData.upsideDown && color==flipView)\r
2854         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2855       else\r
2856         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2857     } else {\r
2858       /* Use square color for details of black pieces */\r
2859       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2860       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2861       if(appData.upsideDown && !flipView)\r
2862         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2863       else\r
2864         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2865     }\r
2866     SelectObject(hdc, oldBrush);\r
2867     SelectObject(tmphdc, oldBitmap);\r
2868   }\r
2869 }\r
2870 \r
2871 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2872 int GetBackTextureMode( int algo )\r
2873 {\r
2874     int result = BACK_TEXTURE_MODE_DISABLED;\r
2875 \r
2876     switch( algo ) \r
2877     {\r
2878         case BACK_TEXTURE_MODE_PLAIN:\r
2879             result = 1; /* Always use identity map */\r
2880             break;\r
2881         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2882             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2883             break;\r
2884     }\r
2885 \r
2886     return result;\r
2887 }\r
2888 \r
2889 /* \r
2890     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2891     to handle redraws cleanly (as random numbers would always be different).\r
2892 */\r
2893 VOID RebuildTextureSquareInfo()\r
2894 {\r
2895     BITMAP bi;\r
2896     int lite_w = 0;\r
2897     int lite_h = 0;\r
2898     int dark_w = 0;\r
2899     int dark_h = 0;\r
2900     int row;\r
2901     int col;\r
2902 \r
2903     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2904 \r
2905     if( liteBackTexture != NULL ) {\r
2906         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2907             lite_w = bi.bmWidth;\r
2908             lite_h = bi.bmHeight;\r
2909         }\r
2910     }\r
2911 \r
2912     if( darkBackTexture != NULL ) {\r
2913         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2914             dark_w = bi.bmWidth;\r
2915             dark_h = bi.bmHeight;\r
2916         }\r
2917     }\r
2918 \r
2919     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2920         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2921             if( (col + row) & 1 ) {\r
2922                 /* Lite square */\r
2923                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2924                   if( lite_w >= squareSize*BOARD_WIDTH )\r
2925                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
2926                   else\r
2927                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2928                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
2929                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
2930                   else\r
2931                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2932                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2933                 }\r
2934             }\r
2935             else {\r
2936                 /* Dark square */\r
2937                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
2938                   if( dark_w >= squareSize*BOARD_WIDTH )\r
2939                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
2940                   else\r
2941                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
2942                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
2943                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
2944                   else\r
2945                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
2946                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
2947                 }\r
2948             }\r
2949         }\r
2950     }\r
2951 }\r
2952 \r
2953 /* [AS] Arrow highlighting support */\r
2954 \r
2955 static double A_WIDTH = 5; /* Width of arrow body */\r
2956 \r
2957 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
2958 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
2959 \r
2960 static double Sqr( double x )\r
2961 {\r
2962     return x*x;\r
2963 }\r
2964 \r
2965 static int Round( double x )\r
2966 {\r
2967     return (int) (x + 0.5);\r
2968 }\r
2969 \r
2970 /* Draw an arrow between two points using current settings */\r
2971 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
2972 {\r
2973     POINT arrow[7];\r
2974     double dx, dy, j, k, x, y;\r
2975 \r
2976     if( d_x == s_x ) {\r
2977         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2978 \r
2979         arrow[0].x = s_x + A_WIDTH + 0.5;\r
2980         arrow[0].y = s_y;\r
2981 \r
2982         arrow[1].x = s_x + A_WIDTH + 0.5;\r
2983         arrow[1].y = d_y - h;\r
2984 \r
2985         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
2986         arrow[2].y = d_y - h;\r
2987 \r
2988         arrow[3].x = d_x;\r
2989         arrow[3].y = d_y;\r
2990 \r
2991         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
2992         arrow[5].y = d_y - h;\r
2993 \r
2994         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
2995         arrow[4].y = d_y - h;\r
2996 \r
2997         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
2998         arrow[6].y = s_y;\r
2999     }\r
3000     else if( d_y == s_y ) {\r
3001         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3002 \r
3003         arrow[0].x = s_x;\r
3004         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3005 \r
3006         arrow[1].x = d_x - w;\r
3007         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3008 \r
3009         arrow[2].x = d_x - w;\r
3010         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3011 \r
3012         arrow[3].x = d_x;\r
3013         arrow[3].y = d_y;\r
3014 \r
3015         arrow[5].x = d_x - w;\r
3016         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3017 \r
3018         arrow[4].x = d_x - w;\r
3019         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3020 \r
3021         arrow[6].x = s_x;\r
3022         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3023     }\r
3024     else {\r
3025         /* [AS] Needed a lot of paper for this! :-) */\r
3026         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3027         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3028   \r
3029         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3030 \r
3031         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3032 \r
3033         x = s_x;\r
3034         y = s_y;\r
3035 \r
3036         arrow[0].x = Round(x - j);\r
3037         arrow[0].y = Round(y + j*dx);\r
3038 \r
3039         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3040         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3041 \r
3042         if( d_x > s_x ) {\r
3043             x = (double) d_x - k;\r
3044             y = (double) d_y - k*dy;\r
3045         }\r
3046         else {\r
3047             x = (double) d_x + k;\r
3048             y = (double) d_y + k*dy;\r
3049         }\r
3050 \r
3051         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3052 \r
3053         arrow[6].x = Round(x - j);\r
3054         arrow[6].y = Round(y + j*dx);\r
3055 \r
3056         arrow[2].x = Round(arrow[6].x + 2*j);\r
3057         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3058 \r
3059         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3060         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3061 \r
3062         arrow[4].x = d_x;\r
3063         arrow[4].y = d_y;\r
3064 \r
3065         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3066         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3067     }\r
3068 \r
3069     Polygon( hdc, arrow, 7 );\r
3070 }\r
3071 \r
3072 /* [AS] Draw an arrow between two squares */\r
3073 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3074 {\r
3075     int s_x, s_y, d_x, d_y;\r
3076     HPEN hpen;\r
3077     HPEN holdpen;\r
3078     HBRUSH hbrush;\r
3079     HBRUSH holdbrush;\r
3080     LOGBRUSH stLB;\r
3081 \r
3082     if( s_col == d_col && s_row == d_row ) {\r
3083         return;\r
3084     }\r
3085 \r
3086     /* Get source and destination points */\r
3087     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3088     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3089 \r
3090     if( d_y > s_y ) {\r
3091         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3092     }\r
3093     else if( d_y < s_y ) {\r
3094         d_y += squareSize / 2 + squareSize / 4;\r
3095     }\r
3096     else {\r
3097         d_y += squareSize / 2;\r
3098     }\r
3099 \r
3100     if( d_x > s_x ) {\r
3101         d_x += squareSize / 2 - squareSize / 4;\r
3102     }\r
3103     else if( d_x < s_x ) {\r
3104         d_x += squareSize / 2 + squareSize / 4;\r
3105     }\r
3106     else {\r
3107         d_x += squareSize / 2;\r
3108     }\r
3109 \r
3110     s_x += squareSize / 2;\r
3111     s_y += squareSize / 2;\r
3112 \r
3113     /* Adjust width */\r
3114     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3115 \r
3116     /* Draw */\r
3117     stLB.lbStyle = BS_SOLID;\r
3118     stLB.lbColor = appData.highlightArrowColor;\r
3119     stLB.lbHatch = 0;\r
3120 \r
3121     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3122     holdpen = SelectObject( hdc, hpen );\r
3123     hbrush = CreateBrushIndirect( &stLB );\r
3124     holdbrush = SelectObject( hdc, hbrush );\r
3125 \r
3126     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3127 \r
3128     SelectObject( hdc, holdpen );\r
3129     SelectObject( hdc, holdbrush );\r
3130     DeleteObject( hpen );\r
3131     DeleteObject( hbrush );\r
3132 }\r
3133 \r
3134 BOOL HasHighlightInfo()\r
3135 {\r
3136     BOOL result = FALSE;\r
3137 \r
3138     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3139         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3140     {\r
3141         result = TRUE;\r
3142     }\r
3143 \r
3144     return result;\r
3145 }\r
3146 \r
3147 BOOL IsDrawArrowEnabled()\r
3148 {\r
3149     BOOL result = FALSE;\r
3150 \r
3151     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3152         result = TRUE;\r
3153     }\r
3154 \r
3155     return result;\r
3156 }\r
3157 \r
3158 VOID DrawArrowHighlight( HDC hdc )\r
3159 {\r
3160     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3161         DrawArrowBetweenSquares( hdc,\r
3162             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3163             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3164     }\r
3165 }\r
3166 \r
3167 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3168 {\r
3169     HRGN result = NULL;\r
3170 \r
3171     if( HasHighlightInfo() ) {\r
3172         int x1, y1, x2, y2;\r
3173         int sx, sy, dx, dy;\r
3174 \r
3175         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3176         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3177 \r
3178         sx = MIN( x1, x2 );\r
3179         sy = MIN( y1, y2 );\r
3180         dx = MAX( x1, x2 ) + squareSize;\r
3181         dy = MAX( y1, y2 ) + squareSize;\r
3182 \r
3183         result = CreateRectRgn( sx, sy, dx, dy );\r
3184     }\r
3185 \r
3186     return result;\r
3187 }\r
3188 \r
3189 /*\r
3190     Warning: this function modifies the behavior of several other functions. \r
3191     \r
3192     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3193     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3194     repaint is scattered all over the place, which is not good for features such as\r
3195     "arrow highlighting" that require a full repaint of the board.\r
3196 \r
3197     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3198     user interaction, when speed is not so important) but especially to avoid errors\r
3199     in the displayed graphics.\r
3200 \r
3201     In such patched places, I always try refer to this function so there is a single\r
3202     place to maintain knowledge.\r
3203     \r
3204     To restore the original behavior, just return FALSE unconditionally.\r
3205 */\r
3206 BOOL IsFullRepaintPreferrable()\r
3207 {\r
3208     BOOL result = FALSE;\r
3209 \r
3210     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3211         /* Arrow may appear on the board */\r
3212         result = TRUE;\r
3213     }\r
3214 \r
3215     return result;\r
3216 }\r
3217 \r
3218 /* \r
3219     This function is called by DrawPosition to know whether a full repaint must\r
3220     be forced or not.\r
3221 \r
3222     Only DrawPosition may directly call this function, which makes use of \r
3223     some state information. Other function should call DrawPosition specifying \r
3224     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3225 */\r
3226 BOOL DrawPositionNeedsFullRepaint()\r
3227 {\r
3228     BOOL result = FALSE;\r
3229 \r
3230     /* \r
3231         Probably a slightly better policy would be to trigger a full repaint\r
3232         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3233         but animation is fast enough that it's difficult to notice.\r
3234     */\r
3235     if( animInfo.piece == EmptySquare ) {\r
3236         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3237             result = TRUE;\r
3238         }\r
3239     }\r
3240 \r
3241     return result;\r
3242 }\r
3243 \r
3244 VOID\r
3245 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3246 {\r
3247   int row, column, x, y, square_color, piece_color;\r
3248   ChessSquare piece;\r
3249   HBRUSH oldBrush;\r
3250   HDC texture_hdc = NULL;\r
3251 \r
3252   /* [AS] Initialize background textures if needed */\r
3253   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3254       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3255       if( backTextureSquareSize != squareSize \r
3256        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3257           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3258           backTextureSquareSize = squareSize;\r
3259           RebuildTextureSquareInfo();\r
3260       }\r
3261 \r
3262       texture_hdc = CreateCompatibleDC( hdc );\r
3263   }\r
3264 \r
3265   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3266     for (column = 0; column < BOARD_WIDTH; column++) {\r
3267   \r
3268       SquareToPos(row, column, &x, &y);\r
3269 \r
3270       piece = board[row][column];\r
3271 \r
3272       square_color = ((column + row) % 2) == 1;\r
3273       if( gameInfo.variant == VariantXiangqi ) {\r
3274           square_color = !InPalace(row, column);\r
3275           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3276           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3277       }\r
3278       piece_color = (int) piece < (int) BlackPawn;\r
3279 \r
3280 \r
3281       /* [HGM] holdings file: light square or black */\r
3282       if(column == BOARD_LEFT-2) {\r
3283             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3284                 square_color = 1;\r
3285             else {\r
3286                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3287                 continue;\r
3288             }\r
3289       } else\r
3290       if(column == BOARD_RGHT + 1 ) {\r
3291             if( row < gameInfo.holdingsSize )\r
3292                 square_color = 1;\r
3293             else {\r
3294                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3295                 continue;\r
3296             }\r
3297       }\r
3298       if(column == BOARD_LEFT-1 ) /* left align */\r
3299             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3300       else if( column == BOARD_RGHT) /* right align */\r
3301             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3302       else\r
3303       if (appData.monoMode) {\r
3304         if (piece == EmptySquare) {\r
3305           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3306                  square_color ? WHITENESS : BLACKNESS);\r
3307         } else {\r
3308           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3309         }\r
3310       } \r
3311       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
3312           /* [AS] Draw the square using a texture bitmap */\r
3313           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3314           int r = row, c = column; // [HGM] do not flip board in flipView\r
3315           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3316 \r
3317           DrawTile( x, y, \r
3318               squareSize, squareSize, \r
3319               hdc, \r
3320               texture_hdc,\r
3321               backTextureSquareInfo[r][c].mode,\r
3322               backTextureSquareInfo[r][c].x,\r
3323               backTextureSquareInfo[r][c].y );\r
3324 \r
3325           SelectObject( texture_hdc, hbm );\r
3326 \r
3327           if (piece != EmptySquare) {\r
3328               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3329           }\r
3330       }\r
3331       else {\r
3332         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3333 \r
3334         oldBrush = SelectObject(hdc, brush );\r
3335         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3336         SelectObject(hdc, oldBrush);\r
3337         if (piece != EmptySquare)\r
3338           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3339       }\r
3340     }\r
3341   }\r
3342 \r
3343   if( texture_hdc != NULL ) {\r
3344     DeleteDC( texture_hdc );\r
3345   }\r
3346 }\r
3347 \r
3348 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3349 void fputDW(FILE *f, int x)\r
3350 {\r
3351         fputc(x     & 255, f);\r
3352         fputc(x>>8  & 255, f);\r
3353         fputc(x>>16 & 255, f);\r
3354         fputc(x>>24 & 255, f);\r
3355 }\r
3356 \r
3357 #define MAX_CLIPS 200   /* more than enough */\r
3358 \r
3359 VOID\r
3360 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3361 {\r
3362 //  HBITMAP bufferBitmap;\r
3363   BITMAP bi;\r
3364 //  RECT Rect;\r
3365   HDC tmphdc;\r
3366   HBITMAP hbm;\r
3367   int w = 100, h = 50;\r
3368 \r
3369   if(logo == NULL) return;\r
3370 //  GetClientRect(hwndMain, &Rect);\r
3371 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3372 //                                      Rect.bottom-Rect.top+1);\r
3373   tmphdc = CreateCompatibleDC(hdc);\r
3374   hbm = SelectObject(tmphdc, logo);\r
3375   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3376             w = bi.bmWidth;\r
3377             h = bi.bmHeight;\r
3378   }\r
3379   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3380                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3381   SelectObject(tmphdc, hbm);\r
3382   DeleteDC(tmphdc);\r
3383 }\r
3384 \r
3385 static HDC hdcSeek;\r
3386 \r
3387 // [HGM] seekgraph\r
3388 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3389 {\r
3390     POINT stPt;\r
3391     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3392     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3393     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3394     SelectObject( hdcSeek, hp );\r
3395 }\r
3396 \r
3397 // front-end wrapper for drawing functions to do rectangles\r
3398 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3399 {\r
3400     HPEN hp;\r
3401     RECT rc;\r
3402 \r
3403     if (hdcSeek == NULL) {\r
3404     hdcSeek = GetDC(hwndMain);\r
3405       if (!appData.monoMode) {\r
3406         SelectPalette(hdcSeek, hPal, FALSE);\r
3407         RealizePalette(hdcSeek);\r
3408       }\r
3409     }\r
3410     hp = SelectObject( hdcSeek, gridPen );\r
3411     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3412     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3413     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3414     SelectObject( hdcSeek, hp );\r
3415 }\r
3416 \r
3417 // front-end wrapper for putting text in graph\r
3418 void DrawSeekText(char *buf, int x, int y)\r
3419 {\r
3420         SIZE stSize;\r
3421         SetBkMode( hdcSeek, TRANSPARENT );\r
3422         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3423         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3424 }\r
3425 \r
3426 void DrawSeekDot(int x, int y, int color)\r
3427 {\r
3428         int square = color & 0x80;\r
3429         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3430                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3431         color &= 0x7F;\r
3432         if(square)\r
3433             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3434                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3435         else\r
3436             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3437                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3438             SelectObject(hdcSeek, oldBrush);\r
3439 }\r
3440 \r
3441 VOID\r
3442 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3443 {\r
3444   static Board lastReq[2], lastDrawn[2];\r
3445   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3446   static int lastDrawnFlipView = 0;\r
3447   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3448   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3449   HDC tmphdc;\r
3450   HDC hdcmem;\r
3451   HBITMAP bufferBitmap;\r
3452   HBITMAP oldBitmap;\r
3453   RECT Rect;\r
3454   HRGN clips[MAX_CLIPS];\r
3455   ChessSquare dragged_piece = EmptySquare;\r
3456   int nr = twoBoards*partnerUp;\r
3457 \r
3458   /* I'm undecided on this - this function figures out whether a full\r
3459    * repaint is necessary on its own, so there's no real reason to have the\r
3460    * caller tell it that.  I think this can safely be set to FALSE - but\r
3461    * if we trust the callers not to request full repaints unnessesarily, then\r
3462    * we could skip some clipping work.  In other words, only request a full\r
3463    * redraw when the majority of pieces have changed positions (ie. flip, \r
3464    * gamestart and similar)  --Hawk\r
3465    */\r
3466   Boolean fullrepaint = repaint;\r
3467 \r
3468   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3469 \r
3470   if( DrawPositionNeedsFullRepaint() ) {\r
3471       fullrepaint = TRUE;\r
3472   }\r
3473 \r
3474   if (board == NULL) {\r
3475     if (!lastReqValid[nr]) {\r
3476       return;\r
3477     }\r
3478     board = lastReq[nr];\r
3479   } else {\r
3480     CopyBoard(lastReq[nr], board);\r
3481     lastReqValid[nr] = 1;\r
3482   }\r
3483 \r
3484   if (doingSizing) {\r
3485     return;\r
3486   }\r
3487 \r
3488   if (IsIconic(hwndMain)) {\r
3489     return;\r
3490   }\r
3491 \r
3492   if (hdc == NULL) {\r
3493     hdc = GetDC(hwndMain);\r
3494     if (!appData.monoMode) {\r
3495       SelectPalette(hdc, hPal, FALSE);\r
3496       RealizePalette(hdc);\r
3497     }\r
3498     releaseDC = TRUE;\r
3499   } else {\r
3500     releaseDC = FALSE;\r
3501   }\r
3502 \r
3503   /* Create some work-DCs */\r
3504   hdcmem = CreateCompatibleDC(hdc);\r
3505   tmphdc = CreateCompatibleDC(hdc);\r
3506 \r
3507   /* If dragging is in progress, we temporarely remove the piece */\r
3508   /* [HGM] or temporarily decrease count if stacked              */\r
3509   /*       !! Moved to before board compare !!                   */\r
3510   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3511     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3512     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3513             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3514         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3515     } else \r
3516     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3517             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3518         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3519     } else \r
3520         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3521   }\r
3522 \r
3523   /* Figure out which squares need updating by comparing the \r
3524    * newest board with the last drawn board and checking if\r
3525    * flipping has changed.\r
3526    */\r
3527   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3528     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3529       for (column = 0; column < BOARD_WIDTH; column++) {\r
3530         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3531           SquareToPos(row, column, &x, &y);\r
3532           clips[num_clips++] =\r
3533             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3534         }\r
3535       }\r
3536     }\r
3537    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3538     for (i=0; i<2; i++) {\r
3539       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3540           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3541         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3542             lastDrawnHighlight.sq[i].y >= 0) {\r
3543           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3544                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3545           clips[num_clips++] =\r
3546             CreateRectRgn(x - lineGap, y - lineGap, \r
3547                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3548         }\r
3549         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3550           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3551           clips[num_clips++] =\r
3552             CreateRectRgn(x - lineGap, y - lineGap, \r
3553                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3554         }\r
3555       }\r
3556     }\r
3557     for (i=0; i<2; i++) {\r
3558       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3559           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3560         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3561             lastDrawnPremove.sq[i].y >= 0) {\r
3562           SquareToPos(lastDrawnPremove.sq[i].y,\r
3563                       lastDrawnPremove.sq[i].x, &x, &y);\r
3564           clips[num_clips++] =\r
3565             CreateRectRgn(x - lineGap, y - lineGap, \r
3566                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3567         }\r
3568         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3569             premoveHighlightInfo.sq[i].y >= 0) {\r
3570           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3571                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3572           clips[num_clips++] =\r
3573             CreateRectRgn(x - lineGap, y - lineGap, \r
3574                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3575         }\r
3576       }\r
3577     }\r
3578    } else { // nr == 1\r
3579         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3580         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3581         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3582         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3583       for (i=0; i<2; i++) {\r
3584         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3585             partnerHighlightInfo.sq[i].y >= 0) {\r
3586           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3587                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3588           clips[num_clips++] =\r
3589             CreateRectRgn(x - lineGap, y - lineGap, \r
3590                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3591         }\r
3592         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3593             oldPartnerHighlight.sq[i].y >= 0) {\r
3594           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3595                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3596           clips[num_clips++] =\r
3597             CreateRectRgn(x - lineGap, y - lineGap, \r
3598                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3599         }\r
3600       }\r
3601    }\r
3602   } else {\r
3603     fullrepaint = TRUE;\r
3604   }\r
3605 \r
3606   /* Create a buffer bitmap - this is the actual bitmap\r
3607    * being written to.  When all the work is done, we can\r
3608    * copy it to the real DC (the screen).  This avoids\r
3609    * the problems with flickering.\r
3610    */\r
3611   GetClientRect(hwndMain, &Rect);\r
3612   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3613                                         Rect.bottom-Rect.top+1);\r
3614   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3615   if (!appData.monoMode) {\r
3616     SelectPalette(hdcmem, hPal, FALSE);\r
3617   }\r
3618 \r
3619   /* Create clips for dragging */\r
3620   if (!fullrepaint) {\r
3621     if (dragInfo.from.x >= 0) {\r
3622       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3623       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3624     }\r
3625     if (dragInfo.start.x >= 0) {\r
3626       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3627       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3628     }\r
3629     if (dragInfo.pos.x >= 0) {\r
3630       x = dragInfo.pos.x - squareSize / 2;\r
3631       y = dragInfo.pos.y - squareSize / 2;\r
3632       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3633     }\r
3634     if (dragInfo.lastpos.x >= 0) {\r
3635       x = dragInfo.lastpos.x - squareSize / 2;\r
3636       y = dragInfo.lastpos.y - squareSize / 2;\r
3637       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3638     }\r
3639   }\r
3640 \r
3641   /* Are we animating a move?  \r
3642    * If so, \r
3643    *   - remove the piece from the board (temporarely)\r
3644    *   - calculate the clipping region\r
3645    */\r
3646   if (!fullrepaint) {\r
3647     if (animInfo.piece != EmptySquare) {\r
3648       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3649       x = boardRect.left + animInfo.lastpos.x;\r
3650       y = boardRect.top + animInfo.lastpos.y;\r
3651       x2 = boardRect.left + animInfo.pos.x;\r
3652       y2 = boardRect.top + animInfo.pos.y;\r
3653       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3654       /* Slight kludge.  The real problem is that after AnimateMove is\r
3655          done, the position on the screen does not match lastDrawn.\r
3656          This currently causes trouble only on e.p. captures in\r
3657          atomic, where the piece moves to an empty square and then\r
3658          explodes.  The old and new positions both had an empty square\r
3659          at the destination, but animation has drawn a piece there and\r
3660          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3661       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3662     }\r
3663   }\r
3664 \r
3665   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3666   if (num_clips == 0)\r
3667     fullrepaint = TRUE;\r
3668 \r
3669   /* Set clipping on the memory DC */\r
3670   if (!fullrepaint) {\r
3671     SelectClipRgn(hdcmem, clips[0]);\r
3672     for (x = 1; x < num_clips; x++) {\r
3673       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3674         abort();  // this should never ever happen!\r
3675     }\r
3676   }\r
3677 \r
3678   /* Do all the drawing to the memory DC */\r
3679   if(explodeInfo.radius) { // [HGM] atomic\r
3680         HBRUSH oldBrush;\r
3681         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3682         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3683         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3684         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3685         x += squareSize/2;\r
3686         y += squareSize/2;\r
3687         if(!fullrepaint) {\r
3688           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3689           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3690         }\r
3691         DrawGridOnDC(hdcmem);\r
3692         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3693         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3694         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3695         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3696         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3697         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3698         SelectObject(hdcmem, oldBrush);\r
3699   } else {\r
3700     DrawGridOnDC(hdcmem);\r
3701     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3702         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3703         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3704     } else {\r
3705         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3706         oldPartnerHighlight = partnerHighlightInfo;\r
3707     }\r
3708     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3709   }\r
3710   if(nr == 0) // [HGM] dual: markers only on left board\r
3711   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3712     for (column = 0; column < BOARD_WIDTH; column++) {\r
3713         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3714             HBRUSH oldBrush = SelectObject(hdcmem, \r
3715                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3716             SquareToPos(row, column, &x, &y);\r
3717             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3718                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3719             SelectObject(hdcmem, oldBrush);\r
3720         }\r
3721     }\r
3722   }\r
3723   if(logoHeight) {\r
3724         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3725         if(appData.autoLogo) {\r
3726           \r
3727           switch(gameMode) { // pick logos based on game mode\r
3728             case IcsObserving:\r
3729                 whiteLogo = second.programLogo; // ICS logo\r
3730                 blackLogo = second.programLogo;\r
3731             default:\r
3732                 break;\r
3733             case IcsPlayingWhite:\r
3734                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3735                 blackLogo = second.programLogo; // ICS logo\r
3736                 break;\r
3737             case IcsPlayingBlack:\r
3738                 whiteLogo = second.programLogo; // ICS logo\r
3739                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3740                 break;\r
3741             case TwoMachinesPlay:\r
3742                 if(first.twoMachinesColor[0] == 'b') {\r
3743                     whiteLogo = second.programLogo;\r
3744                     blackLogo = first.programLogo;\r
3745                 }\r
3746                 break;\r
3747             case MachinePlaysWhite:\r
3748                 blackLogo = userLogo;\r
3749                 break;\r
3750             case MachinePlaysBlack:\r
3751                 whiteLogo = userLogo;\r
3752                 blackLogo = first.programLogo;\r
3753           }\r
3754         }\r
3755         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3756         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3757   }\r
3758 \r
3759   if( appData.highlightMoveWithArrow ) {\r
3760     DrawArrowHighlight(hdcmem);\r
3761   }\r
3762 \r
3763   DrawCoordsOnDC(hdcmem);\r
3764 \r
3765   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3766                  /* to make sure lastDrawn contains what is actually drawn */\r
3767 \r
3768   /* Put the dragged piece back into place and draw it (out of place!) */\r
3769     if (dragged_piece != EmptySquare) {\r
3770     /* [HGM] or restack */\r
3771     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3772                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3773     else\r
3774     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3775                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3776     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3777     x = dragInfo.pos.x - squareSize / 2;\r
3778     y = dragInfo.pos.y - squareSize / 2;\r
3779     DrawPieceOnDC(hdcmem, dragged_piece,\r
3780                   ((int) dragged_piece < (int) BlackPawn), \r
3781                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3782   }   \r
3783   \r
3784   /* Put the animated piece back into place and draw it */\r
3785   if (animInfo.piece != EmptySquare) {\r
3786     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3787     x = boardRect.left + animInfo.pos.x;\r
3788     y = boardRect.top + animInfo.pos.y;\r
3789     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3790                   ((int) animInfo.piece < (int) BlackPawn),\r
3791                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3792   }\r
3793 \r
3794   /* Release the bufferBitmap by selecting in the old bitmap \r
3795    * and delete the memory DC\r
3796    */\r
3797   SelectObject(hdcmem, oldBitmap);\r
3798   DeleteDC(hdcmem);\r
3799 \r
3800   /* Set clipping on the target DC */\r
3801   if (!fullrepaint) {\r
3802     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3803         RECT rect;\r
3804         GetRgnBox(clips[x], &rect);\r
3805         DeleteObject(clips[x]);\r
3806         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3807                           rect.right + wpMain.width/2, rect.bottom);\r
3808     }\r
3809     SelectClipRgn(hdc, clips[0]);\r
3810     for (x = 1; x < num_clips; x++) {\r
3811       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3812         abort();   // this should never ever happen!\r
3813     } \r
3814   }\r
3815 \r
3816   /* Copy the new bitmap onto the screen in one go.\r
3817    * This way we avoid any flickering\r
3818    */\r
3819   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3820   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3821          boardRect.right - boardRect.left,\r
3822          boardRect.bottom - boardRect.top,\r
3823          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3824   if(saveDiagFlag) { \r
3825     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
3826     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3827 \r
3828     GetObject(bufferBitmap, sizeof(b), &b);\r
3829     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
3830         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3831         bih.biWidth = b.bmWidth;\r
3832         bih.biHeight = b.bmHeight;\r
3833         bih.biPlanes = 1;\r
3834         bih.biBitCount = b.bmBitsPixel;\r
3835         bih.biCompression = 0;\r
3836         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3837         bih.biXPelsPerMeter = 0;\r
3838         bih.biYPelsPerMeter = 0;\r
3839         bih.biClrUsed = 0;\r
3840         bih.biClrImportant = 0;\r
3841 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3842 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3843         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3844 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3845 \r
3846         wb = b.bmWidthBytes;\r
3847         // count colors\r
3848         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3849                 int k = ((int*) pData)[i];\r
3850                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3851                 if(j >= 16) break;\r
3852                 color[j] = k;\r
3853                 if(j >= nrColors) nrColors = j+1;\r
3854         }\r
3855         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3856                 INT p = 0;\r
3857                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3858                     for(w=0; w<(wb>>2); w+=2) {\r
3859                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3860                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3861                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3862                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3863                         pData[p++] = m | j<<4;\r
3864                     }\r
3865                     while(p&3) pData[p++] = 0;\r
3866                 }\r
3867                 fac = 3;\r
3868                 wb = ((wb+31)>>5)<<2;\r
3869         }\r
3870         // write BITMAPFILEHEADER\r
3871         fprintf(diagFile, "BM");\r
3872         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3873         fputDW(diagFile, 0);\r
3874         fputDW(diagFile, 0x36 + (fac?64:0));\r
3875         // write BITMAPINFOHEADER\r
3876         fputDW(diagFile, 40);\r
3877         fputDW(diagFile, b.bmWidth);\r
3878         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3879         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3880         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3881         fputDW(diagFile, 0);\r
3882         fputDW(diagFile, 0);\r
3883         fputDW(diagFile, 0);\r
3884         fputDW(diagFile, 0);\r
3885         fputDW(diagFile, 0);\r
3886         fputDW(diagFile, 0);\r
3887         // write color table\r
3888         if(fac)\r
3889         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3890         // write bitmap data\r
3891         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3892                 fputc(pData[i], diagFile);\r
3893      }\r
3894   }\r
3895 \r
3896   SelectObject(tmphdc, oldBitmap);\r
3897 \r
3898   /* Massive cleanup */\r
3899   for (x = 0; x < num_clips; x++)\r
3900     DeleteObject(clips[x]);\r
3901 \r
3902   DeleteDC(tmphdc);\r
3903   DeleteObject(bufferBitmap);\r
3904 \r
3905   if (releaseDC) \r
3906     ReleaseDC(hwndMain, hdc);\r
3907   \r
3908   if (lastDrawnFlipView != flipView && nr == 0) {\r
3909     if (flipView)\r
3910       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3911     else\r
3912       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3913   }\r
3914 \r
3915 /*  CopyBoard(lastDrawn, board);*/\r
3916   lastDrawnHighlight = highlightInfo;\r
3917   lastDrawnPremove   = premoveHighlightInfo;\r
3918   lastDrawnFlipView = flipView;\r
3919   lastDrawnValid[nr] = 1;\r
3920 }\r
3921 \r
3922 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3923 int\r
3924 SaveDiagram(f)\r
3925      FILE *f;\r
3926 {\r
3927     saveDiagFlag = 1; diagFile = f;\r
3928     HDCDrawPosition(NULL, TRUE, NULL);\r
3929 \r
3930     saveDiagFlag = 0;\r
3931 \r
3932 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
3933     \r
3934     fclose(f);\r
3935     return TRUE;\r
3936 }\r
3937 \r
3938 \r
3939 /*---------------------------------------------------------------------------*\\r
3940 | CLIENT PAINT PROCEDURE\r
3941 |   This is the main event-handler for the WM_PAINT message.\r
3942 |\r
3943 \*---------------------------------------------------------------------------*/\r
3944 VOID\r
3945 PaintProc(HWND hwnd)\r
3946 {\r
3947   HDC         hdc;\r
3948   PAINTSTRUCT ps;\r
3949   HFONT       oldFont;\r
3950 \r
3951   if((hdc = BeginPaint(hwnd, &ps))) {\r
3952     if (IsIconic(hwnd)) {\r
3953       DrawIcon(hdc, 2, 2, iconCurrent);\r
3954     } else {\r
3955       if (!appData.monoMode) {\r
3956         SelectPalette(hdc, hPal, FALSE);\r
3957         RealizePalette(hdc);\r
3958       }\r
3959       HDCDrawPosition(hdc, 1, NULL);\r
3960       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
3961         flipView = !flipView; partnerUp = !partnerUp;\r
3962         HDCDrawPosition(hdc, 1, NULL);\r
3963         flipView = !flipView; partnerUp = !partnerUp;\r
3964       }\r
3965       oldFont =\r
3966         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3967       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
3968                  ETO_CLIPPED|ETO_OPAQUE,\r
3969                  &messageRect, messageText, strlen(messageText), NULL);\r
3970       SelectObject(hdc, oldFont);\r
3971       DisplayBothClocks();\r
3972     }\r
3973     EndPaint(hwnd,&ps);\r
3974   }\r
3975 \r
3976   return;\r
3977 }\r
3978 \r
3979 \r
3980 /*\r
3981  * If the user selects on a border boundary, return -1; if off the board,\r
3982  *   return -2.  Otherwise map the event coordinate to the square.\r
3983  * The offset boardRect.left or boardRect.top must already have been\r
3984  *   subtracted from x.\r
3985  */\r
3986 int EventToSquare(x, limit)\r
3987      int x, limit;\r
3988 {\r
3989   if (x <= 0)\r
3990     return -2;\r
3991   if (x < lineGap)\r
3992     return -1;\r
3993   x -= lineGap;\r
3994   if ((x % (squareSize + lineGap)) >= squareSize)\r
3995     return -1;\r
3996   x /= (squareSize + lineGap);\r
3997     if (x >= limit)\r
3998     return -2;\r
3999   return x;\r
4000 }\r
4001 \r
4002 typedef struct {\r
4003   char piece;\r
4004   int command;\r
4005   char* name;\r
4006 } DropEnable;\r
4007 \r
4008 DropEnable dropEnables[] = {\r
4009   { 'P', DP_Pawn, N_("Pawn") },\r
4010   { 'N', DP_Knight, N_("Knight") },\r
4011   { 'B', DP_Bishop, N_("Bishop") },\r
4012   { 'R', DP_Rook, N_("Rook") },\r
4013   { 'Q', DP_Queen, N_("Queen") },\r
4014 };\r
4015 \r
4016 VOID\r
4017 SetupDropMenu(HMENU hmenu)\r
4018 {\r
4019   int i, count, enable;\r
4020   char *p;\r
4021   extern char white_holding[], black_holding[];\r
4022   char item[MSG_SIZ];\r
4023 \r
4024   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4025     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4026                dropEnables[i].piece);\r
4027     count = 0;\r
4028     while (p && *p++ == dropEnables[i].piece) count++;\r
4029       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4030     enable = count > 0 || !appData.testLegality\r
4031       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4032                       && !appData.icsActive);\r
4033     ModifyMenu(hmenu, dropEnables[i].command,\r
4034                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4035                dropEnables[i].command, item);\r
4036   }\r
4037 }\r
4038 \r
4039 void DragPieceBegin(int x, int y)\r
4040 {\r
4041       dragInfo.lastpos.x = boardRect.left + x;\r
4042       dragInfo.lastpos.y = boardRect.top + y;\r
4043       dragInfo.from.x = fromX;\r
4044       dragInfo.from.y = fromY;\r
4045       dragInfo.start = dragInfo.from;\r
4046       SetCapture(hwndMain);\r
4047 }\r
4048 \r
4049 void DragPieceEnd(int x, int y)\r
4050 {\r
4051     ReleaseCapture();\r
4052     dragInfo.start.x = dragInfo.start.y = -1;\r
4053     dragInfo.from = dragInfo.start;\r
4054     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4055 }\r
4056 \r
4057 /* Event handler for mouse messages */\r
4058 VOID\r
4059 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4060 {\r
4061   int x, y, menuNr;\r
4062   POINT pt;\r
4063   static int recursive = 0;\r
4064   HMENU hmenu;\r
4065   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4066 \r
4067   if (recursive) {\r
4068     if (message == WM_MBUTTONUP) {\r
4069       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4070          to the middle button: we simulate pressing the left button too!\r
4071          */\r
4072       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4073       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4074     }\r
4075     return;\r
4076   }\r
4077   recursive++;\r
4078   \r
4079   pt.x = LOWORD(lParam);\r
4080   pt.y = HIWORD(lParam);\r
4081   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4082   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4083   if (!flipView && y >= 0) {\r
4084     y = BOARD_HEIGHT - 1 - y;\r
4085   }\r
4086   if (flipView && x >= 0) {\r
4087     x = BOARD_WIDTH - 1 - x;\r
4088   }\r
4089 \r
4090   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4091 \r
4092   switch (message) {\r
4093   case WM_LBUTTONDOWN:\r
4094       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4095         if (gameMode == EditPosition) {\r
4096           SetWhiteToPlayEvent();\r
4097         } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {\r
4098           AdjustClock(flipClock, -1);\r
4099         } else if (gameMode == IcsPlayingBlack ||\r
4100                    gameMode == MachinePlaysWhite) {\r
4101           CallFlagEvent();\r
4102         }\r
4103       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4104         if (gameMode == EditPosition) {\r
4105           SetBlackToPlayEvent();\r
4106         } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {\r
4107           AdjustClock(!flipClock, -1);\r
4108         } else if (gameMode == IcsPlayingWhite ||\r
4109                    gameMode == MachinePlaysBlack) {\r
4110           CallFlagEvent();\r
4111         }\r
4112       }\r
4113       dragInfo.start.x = dragInfo.start.y = -1;\r
4114       dragInfo.from = dragInfo.start;\r
4115     if(fromX == -1 && frozen) { // not sure where this is for\r
4116                 fromX = fromY = -1; \r
4117       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4118       break;\r
4119     }\r
4120       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4121       DrawPosition(TRUE, NULL);\r
4122     break;\r
4123 \r
4124   case WM_LBUTTONUP:\r
4125       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4126       DrawPosition(TRUE, NULL);\r
4127     break;\r
4128 \r
4129   case WM_MOUSEMOVE:\r
4130     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4131     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4132     if ((appData.animateDragging || appData.highlightDragging)\r
4133         && (wParam & MK_LBUTTON)\r
4134         && dragInfo.from.x >= 0) \r
4135     {\r
4136       BOOL full_repaint = FALSE;\r
4137 \r
4138       if (appData.animateDragging) {\r
4139         dragInfo.pos = pt;\r
4140       }\r
4141       if (appData.highlightDragging) {\r
4142         SetHighlights(fromX, fromY, x, y);\r
4143         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4144             full_repaint = TRUE;\r
4145         }\r
4146       }\r
4147       \r
4148       DrawPosition( full_repaint, NULL);\r
4149       \r
4150       dragInfo.lastpos = dragInfo.pos;\r
4151     }\r
4152     break;\r
4153 \r
4154   case WM_MOUSEWHEEL: // [DM]\r
4155     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4156        /* Mouse Wheel is being rolled forward\r
4157         * Play moves forward\r
4158         */\r
4159        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4160                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4161        /* Mouse Wheel is being rolled backward\r
4162         * Play moves backward\r
4163         */\r
4164        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4165                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4166     }\r
4167     break;\r
4168 \r
4169   case WM_MBUTTONUP:\r
4170   case WM_RBUTTONUP:\r
4171     ReleaseCapture();\r
4172     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4173     break;\r
4174  \r
4175   case WM_MBUTTONDOWN:\r
4176   case WM_RBUTTONDOWN:\r
4177     ErrorPopDown();\r
4178     ReleaseCapture();\r
4179     fromX = fromY = -1;\r
4180     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4181     dragInfo.start.x = dragInfo.start.y = -1;\r
4182     dragInfo.from = dragInfo.start;\r
4183     dragInfo.lastpos = dragInfo.pos;\r
4184     if (appData.highlightDragging) {\r
4185       ClearHighlights();\r
4186     }\r
4187     if(y == -2) {\r
4188       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4189       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4190           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4191       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4192           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4193       }\r
4194       break;\r
4195     }\r
4196     DrawPosition(TRUE, NULL);\r
4197 \r
4198     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4199     switch (menuNr) {\r
4200     case 0:\r
4201       if (message == WM_MBUTTONDOWN) {\r
4202         buttonCount = 3;  /* even if system didn't think so */\r
4203         if (wParam & MK_SHIFT) \r
4204           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4205         else\r
4206           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4207       } else { /* message == WM_RBUTTONDOWN */\r
4208         /* Just have one menu, on the right button.  Windows users don't\r
4209            think to try the middle one, and sometimes other software steals\r
4210            it, or it doesn't really exist. */\r
4211         if(gameInfo.variant != VariantShogi)\r
4212             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4213         else\r
4214             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4215       }\r
4216       break;\r
4217     case 2:\r
4218       SetCapture(hwndMain);
4219       break;\r
4220     case 1:\r
4221       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4222       SetupDropMenu(hmenu);\r
4223       MenuPopup(hwnd, pt, hmenu, -1);\r
4224     default:\r
4225       break;\r
4226     }\r
4227     break;\r
4228   }\r
4229 \r
4230   recursive--;\r
4231 }\r
4232 \r
4233 /* Preprocess messages for buttons in main window */\r
4234 LRESULT CALLBACK\r
4235 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4236 {\r
4237   int id = GetWindowLong(hwnd, GWL_ID);\r
4238   int i, dir;\r
4239 \r
4240   for (i=0; i<N_BUTTONS; i++) {\r
4241     if (buttonDesc[i].id == id) break;\r
4242   }\r
4243   if (i == N_BUTTONS) return 0;\r
4244   switch (message) {\r
4245   case WM_KEYDOWN:\r
4246     switch (wParam) {\r
4247     case VK_LEFT:\r
4248     case VK_RIGHT:\r
4249       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4250       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4251       return TRUE;\r
4252     }\r
4253     break;\r
4254   case WM_CHAR:\r
4255     switch (wParam) {\r
4256     case '\r':\r
4257       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4258       return TRUE;\r
4259     default:\r
4260       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4261         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4262         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4263         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4264         SetFocus(h);\r
4265         SendMessage(h, WM_CHAR, wParam, lParam);\r
4266         return TRUE;\r
4267       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4268         PopUpMoveDialog((char)wParam);\r
4269       }\r
4270       break;\r
4271     }\r
4272     break;\r
4273   }\r
4274   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4275 }\r
4276 \r
4277 /* Process messages for Promotion dialog box */\r
4278 LRESULT CALLBACK\r
4279 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4280 {\r
4281   char promoChar;\r
4282 \r
4283   switch (message) {\r
4284   case WM_INITDIALOG: /* message: initialize dialog box */\r
4285     /* Center the dialog over the application window */\r
4286     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4287     Translate(hDlg, DLG_PromotionKing);\r
4288     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4289       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4290        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4291                SW_SHOW : SW_HIDE);\r
4292     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4293     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4294        ((PieceToChar(WhiteAngel) >= 'A' &&\r
4295          PieceToChar(WhiteAngel) != '~') ||\r
4296         (PieceToChar(BlackAngel) >= 'A' &&\r
4297          PieceToChar(BlackAngel) != '~')   ) ?\r
4298                SW_SHOW : SW_HIDE);\r
4299     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4300        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
4301          PieceToChar(WhiteMarshall) != '~') ||\r
4302         (PieceToChar(BlackMarshall) >= 'A' &&\r
4303          PieceToChar(BlackMarshall) != '~')   ) ?\r
4304                SW_SHOW : SW_HIDE);\r
4305     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4306     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4307        gameInfo.variant != VariantShogi ?\r
4308                SW_SHOW : SW_HIDE);\r
4309     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4310        gameInfo.variant != VariantShogi ?\r
4311                SW_SHOW : SW_HIDE);\r
4312     if(gameInfo.variant == VariantShogi) {\r
4313         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4314         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4315         SetWindowText(hDlg, "Promote?");\r
4316     }\r
4317     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4318        gameInfo.variant == VariantSuper ?\r
4319                SW_SHOW : SW_HIDE);\r
4320     return TRUE;\r
4321 \r
4322   case WM_COMMAND: /* message: received a command */\r
4323     switch (LOWORD(wParam)) {\r
4324     case IDCANCEL:\r
4325       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4326       ClearHighlights();\r
4327       DrawPosition(FALSE, NULL);\r
4328       return TRUE;\r
4329     case PB_King:\r
4330       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4331       break;\r
4332     case PB_Queen:\r
4333       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
4334       break;\r
4335     case PB_Rook:\r
4336       promoChar = PieceToChar(BlackRook);\r
4337       break;\r
4338     case PB_Bishop:\r
4339       promoChar = PieceToChar(BlackBishop);\r
4340       break;\r
4341     case PB_Chancellor:\r
4342       promoChar = PieceToChar(BlackMarshall);\r
4343       break;\r
4344     case PB_Archbishop:\r
4345       promoChar = PieceToChar(BlackAngel);\r
4346       break;\r
4347     case PB_Knight:\r
4348       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
4349       break;\r
4350     default:\r
4351       return FALSE;\r
4352     }\r
4353     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4354     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
4355        only show the popup when we are already sure the move is valid or\r
4356        legal. We pass a faulty move type, but the kludge is that FinishMove\r
4357        will figure out it is a promotion from the promoChar. */\r
4358     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4359     fromX = fromY = -1;\r
4360     if (!appData.highlightLastMove) {\r
4361       ClearHighlights();\r
4362       DrawPosition(FALSE, NULL);\r
4363     }\r
4364     return TRUE;\r
4365   }\r
4366   return FALSE;\r
4367 }\r
4368 \r
4369 /* Pop up promotion dialog */\r
4370 VOID\r
4371 PromotionPopup(HWND hwnd)\r
4372 {\r
4373   FARPROC lpProc;\r
4374 \r
4375   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4376   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4377     hwnd, (DLGPROC)lpProc);\r
4378   FreeProcInstance(lpProc);\r
4379 }\r
4380 \r
4381 void\r
4382 PromotionPopUp()\r
4383 {\r
4384   DrawPosition(TRUE, NULL);\r
4385   PromotionPopup(hwndMain);\r
4386 }\r
4387 \r
4388 /* Toggle ShowThinking */\r
4389 VOID\r
4390 ToggleShowThinking()\r
4391 {\r
4392   appData.showThinking = !appData.showThinking;\r
4393   ShowThinkingEvent();\r
4394 }\r
4395 \r
4396 VOID\r
4397 LoadGameDialog(HWND hwnd, char* title)\r
4398 {\r
4399   UINT number = 0;\r
4400   FILE *f;\r
4401   char fileTitle[MSG_SIZ];\r
4402   f = OpenFileDialog(hwnd, "rb", "",\r
4403                      appData.oldSaveStyle ? "gam" : "pgn",\r
4404                      GAME_FILT,\r
4405                      title, &number, fileTitle, NULL);\r
4406   if (f != NULL) {\r
4407     cmailMsgLoaded = FALSE;\r
4408     if (number == 0) {\r
4409       int error = GameListBuild(f);\r
4410       if (error) {\r
4411         DisplayError(_("Cannot build game list"), error);\r
4412       } else if (!ListEmpty(&gameList) &&\r
4413                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4414         GameListPopUp(f, fileTitle);\r
4415         return;\r
4416       }\r
4417       GameListDestroy();\r
4418       number = 1;\r
4419     }\r
4420     LoadGame(f, number, fileTitle, FALSE);\r
4421   }\r
4422 }\r
4423 \r
4424 int get_term_width()\r
4425 {\r
4426     HDC hdc;\r
4427     TEXTMETRIC tm;\r
4428     RECT rc;\r
4429     HFONT hfont, hold_font;\r
4430     LOGFONT lf;\r
4431     HWND hText;\r
4432 \r
4433     if (hwndConsole)\r
4434         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4435     else\r
4436         return 79;\r
4437 \r
4438     // get the text metrics\r
4439     hdc = GetDC(hText);\r
4440     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4441     if (consoleCF.dwEffects & CFE_BOLD)\r
4442         lf.lfWeight = FW_BOLD;\r
4443     if (consoleCF.dwEffects & CFE_ITALIC)\r
4444         lf.lfItalic = TRUE;\r
4445     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4446         lf.lfStrikeOut = TRUE;\r
4447     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4448         lf.lfUnderline = TRUE;\r
4449     hfont = CreateFontIndirect(&lf);\r
4450     hold_font = SelectObject(hdc, hfont);\r
4451     GetTextMetrics(hdc, &tm);\r
4452     SelectObject(hdc, hold_font);\r
4453     DeleteObject(hfont);\r
4454     ReleaseDC(hText, hdc);\r
4455 \r
4456     // get the rectangle\r
4457     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4458 \r
4459     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4460 }\r
4461 \r
4462 void UpdateICSWidth(HWND hText)\r
4463 {\r
4464     LONG old_width, new_width;\r
4465 \r
4466     new_width = get_term_width(hText, FALSE);\r
4467     old_width = GetWindowLong(hText, GWL_USERDATA);\r
4468     if (new_width != old_width)\r
4469     {\r
4470         ics_update_width(new_width);\r
4471         SetWindowLong(hText, GWL_USERDATA, new_width);\r
4472     }\r
4473 }\r
4474 \r
4475 VOID\r
4476 ChangedConsoleFont()\r
4477 {\r
4478   CHARFORMAT cfmt;\r
4479   CHARRANGE tmpsel, sel;\r
4480   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4481   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4482   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4483   PARAFORMAT paraf;\r
4484 \r
4485   cfmt.cbSize = sizeof(CHARFORMAT);\r
4486   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4487     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4488                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4489   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4490    * size.  This was undocumented in the version of MSVC++ that I had\r
4491    * when I wrote the code, but is apparently documented now.\r
4492    */\r
4493   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4494   cfmt.bCharSet = f->lf.lfCharSet;\r
4495   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4496   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4497   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4498   /* Why are the following seemingly needed too? */\r
4499   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4500   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4501   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4502   tmpsel.cpMin = 0;\r
4503   tmpsel.cpMax = -1; /*999999?*/\r
4504   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4505   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4506   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4507    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4508    */\r
4509   paraf.cbSize = sizeof(paraf);\r
4510   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4511   paraf.dxStartIndent = 0;\r
4512   paraf.dxOffset = WRAP_INDENT;\r
4513   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4514   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4515   UpdateICSWidth(hText);\r
4516 }\r
4517 \r
4518 /*---------------------------------------------------------------------------*\\r
4519  *\r
4520  * Window Proc for main window\r
4521  *\r
4522 \*---------------------------------------------------------------------------*/\r
4523 \r
4524 /* Process messages for main window, etc. */\r
4525 LRESULT CALLBACK\r
4526 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4527 {\r
4528   FARPROC lpProc;\r
4529   int wmId, wmEvent;\r
4530   char *defName;\r
4531   FILE *f;\r
4532   UINT number;\r
4533   char fileTitle[MSG_SIZ];\r
4534   char buf[MSG_SIZ];\r
4535   static SnapData sd;\r
4536 \r
4537   switch (message) {\r
4538 \r
4539   case WM_PAINT: /* message: repaint portion of window */\r
4540     PaintProc(hwnd);\r
4541     break;\r
4542 \r
4543   case WM_ERASEBKGND:\r
4544     if (IsIconic(hwnd)) {\r
4545       /* Cheat; change the message */\r
4546       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4547     } else {\r
4548       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4549     }\r
4550     break;\r
4551 \r
4552   case WM_LBUTTONDOWN:\r
4553   case WM_MBUTTONDOWN:\r
4554   case WM_RBUTTONDOWN:\r
4555   case WM_LBUTTONUP:\r
4556   case WM_MBUTTONUP:\r
4557   case WM_RBUTTONUP:\r
4558   case WM_MOUSEMOVE:\r
4559   case WM_MOUSEWHEEL:\r
4560     MouseEvent(hwnd, message, wParam, lParam);\r
4561     break;\r
4562 \r
4563   JAWS_KB_NAVIGATION\r
4564 \r
4565   case WM_CHAR:\r
4566     \r
4567     JAWS_ALT_INTERCEPT\r
4568 \r
4569     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4570         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4571         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4572         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4573         SetFocus(h);\r
4574         SendMessage(h, message, wParam, lParam);\r
4575     } else if(lParam != KF_REPEAT) {\r
4576         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4577                 PopUpMoveDialog((char)wParam);\r
4578         } else if((char)wParam == 003) CopyGameToClipboard();\r
4579          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4580     }\r
4581 \r
4582     break;\r
4583 \r
4584   case WM_PALETTECHANGED:\r
4585     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4586       int nnew;\r
4587       HDC hdc = GetDC(hwndMain);\r
4588       SelectPalette(hdc, hPal, TRUE);\r
4589       nnew = RealizePalette(hdc);\r
4590       if (nnew > 0) {\r
4591         paletteChanged = TRUE;\r
4592         InvalidateRect(hwnd, &boardRect, FALSE);\r
4593       }\r
4594       ReleaseDC(hwnd, hdc);\r
4595     }\r
4596     break;\r
4597 \r
4598   case WM_QUERYNEWPALETTE:\r
4599     if (!appData.monoMode /*&& paletteChanged*/) {\r
4600       int nnew;\r
4601       HDC hdc = GetDC(hwndMain);\r
4602       paletteChanged = FALSE;\r
4603       SelectPalette(hdc, hPal, FALSE);\r
4604       nnew = RealizePalette(hdc);\r
4605       if (nnew > 0) {\r
4606         InvalidateRect(hwnd, &boardRect, FALSE);\r
4607       }\r
4608       ReleaseDC(hwnd, hdc);\r
4609       return TRUE;\r
4610     }\r
4611     return FALSE;\r
4612 \r
4613   case WM_COMMAND: /* message: command from application menu */\r
4614     wmId    = LOWORD(wParam);\r
4615     wmEvent = HIWORD(wParam);\r
4616 \r
4617     switch (wmId) {\r
4618     case IDM_NewGame:\r
4619       ResetGameEvent();\r
4620       SAY("new game enter a move to play against the computer with white");\r
4621       break;\r
4622 \r
4623     case IDM_NewGameFRC:\r
4624       if( NewGameFRC() == 0 ) {\r
4625         ResetGameEvent();\r
4626       }\r
4627       break;\r
4628 \r
4629     case IDM_NewVariant:\r
4630       NewVariantPopup(hwnd);\r
4631       break;\r
4632 \r
4633     case IDM_LoadGame:\r
4634       LoadGameDialog(hwnd, _("Load Game from File"));\r
4635       break;\r
4636 \r
4637     case IDM_LoadNextGame:\r
4638       ReloadGame(1);\r
4639       break;\r
4640 \r
4641     case IDM_LoadPrevGame:\r
4642       ReloadGame(-1);\r
4643       break;\r
4644 \r
4645     case IDM_ReloadGame:\r
4646       ReloadGame(0);\r
4647       break;\r
4648 \r
4649     case IDM_LoadPosition:\r
4650       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4651         Reset(FALSE, TRUE);\r
4652       }\r
4653       number = 1;\r
4654       f = OpenFileDialog(hwnd, "rb", "",\r
4655                          appData.oldSaveStyle ? "pos" : "fen",\r
4656                          POSITION_FILT,\r
4657                          _("Load Position from File"), &number, fileTitle, NULL);\r
4658       if (f != NULL) {\r
4659         LoadPosition(f, number, fileTitle);\r
4660       }\r
4661       break;\r
4662 \r
4663     case IDM_LoadNextPosition:\r
4664       ReloadPosition(1);\r
4665       break;\r
4666 \r
4667     case IDM_LoadPrevPosition:\r
4668       ReloadPosition(-1);\r
4669       break;\r
4670 \r
4671     case IDM_ReloadPosition:\r
4672       ReloadPosition(0);\r
4673       break;\r
4674 \r
4675     case IDM_SaveGame:\r
4676       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4677       f = OpenFileDialog(hwnd, "a", defName,\r
4678                          appData.oldSaveStyle ? "gam" : "pgn",\r
4679                          GAME_FILT,\r
4680                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4681       if (f != NULL) {\r
4682         SaveGame(f, 0, "");\r
4683       }\r
4684       break;\r
4685 \r
4686     case IDM_SavePosition:\r
4687       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4688       f = OpenFileDialog(hwnd, "a", defName,\r
4689                          appData.oldSaveStyle ? "pos" : "fen",\r
4690                          POSITION_FILT,\r
4691                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4692       if (f != NULL) {\r
4693         SavePosition(f, 0, "");\r
4694       }\r
4695       break;\r
4696 \r
4697     case IDM_SaveDiagram:\r
4698       defName = "diagram";\r
4699       f = OpenFileDialog(hwnd, "wb", defName,\r
4700                          "bmp",\r
4701                          DIAGRAM_FILT,\r
4702                          "Save Diagram to File", NULL, fileTitle, NULL);\r
4703       if (f != NULL) {\r
4704         SaveDiagram(f);\r
4705       }\r
4706       break;\r
4707 \r
4708     case IDM_CopyGame:\r
4709       CopyGameToClipboard();\r
4710       break;\r
4711 \r
4712     case IDM_PasteGame:\r
4713       PasteGameFromClipboard();\r
4714       break;\r
4715 \r
4716     case IDM_CopyGameListToClipboard:\r
4717       CopyGameListToClipboard();\r
4718       break;\r
4719 \r
4720     /* [AS] Autodetect FEN or PGN data */\r
4721     case IDM_PasteAny:\r
4722       PasteGameOrFENFromClipboard();\r
4723       break;\r
4724 \r
4725     /* [AS] Move history */\r
4726     case IDM_ShowMoveHistory:\r
4727         if( MoveHistoryIsUp() ) {\r
4728             MoveHistoryPopDown();\r
4729         }\r
4730         else {\r
4731             MoveHistoryPopUp();\r
4732         }\r
4733         break;\r
4734 \r
4735     /* [AS] Eval graph */\r
4736     case IDM_ShowEvalGraph:\r
4737         if( EvalGraphIsUp() ) {\r
4738             EvalGraphPopDown();\r
4739         }\r
4740         else {\r
4741             EvalGraphPopUp();\r
4742             SetFocus(hwndMain);\r
4743         }\r
4744         break;\r
4745 \r
4746     /* [AS] Engine output */\r
4747     case IDM_ShowEngineOutput:\r
4748         if( EngineOutputIsUp() ) {\r
4749             EngineOutputPopDown();\r
4750         }\r
4751         else {\r
4752             EngineOutputPopUp();\r
4753         }\r
4754         break;\r
4755 \r
4756     /* [AS] User adjudication */\r
4757     case IDM_UserAdjudication_White:\r
4758         UserAdjudicationEvent( +1 );\r
4759         break;\r
4760 \r
4761     case IDM_UserAdjudication_Black:\r
4762         UserAdjudicationEvent( -1 );\r
4763         break;\r
4764 \r
4765     case IDM_UserAdjudication_Draw:\r
4766         UserAdjudicationEvent( 0 );\r
4767         break;\r
4768 \r
4769     /* [AS] Game list options dialog */\r
4770     case IDM_GameListOptions:\r
4771       GameListOptions();\r
4772       break;\r
4773 \r
4774     case IDM_NewChat:\r
4775       ChatPopUp(NULL);\r
4776       break;\r
4777 \r
4778     case IDM_CopyPosition:\r
4779       CopyFENToClipboard();\r
4780       break;\r
4781 \r
4782     case IDM_PastePosition:\r
4783       PasteFENFromClipboard();\r
4784       break;\r
4785 \r
4786     case IDM_MailMove:\r
4787       MailMoveEvent();\r
4788       break;\r
4789 \r
4790     case IDM_ReloadCMailMsg:\r
4791       Reset(TRUE, TRUE);\r
4792       ReloadCmailMsgEvent(FALSE);\r
4793       break;\r
4794 \r
4795     case IDM_Minimize:\r
4796       ShowWindow(hwnd, SW_MINIMIZE);\r
4797       break;\r
4798 \r
4799     case IDM_Exit:\r
4800       ExitEvent(0);\r
4801       break;\r
4802 \r
4803     case IDM_MachineWhite:\r
4804       MachineWhiteEvent();\r
4805       /*\r
4806        * refresh the tags dialog only if it's visible\r
4807        */\r
4808       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4809           char *tags;\r
4810           tags = PGNTags(&gameInfo);\r
4811           TagsPopUp(tags, CmailMsg());\r
4812           free(tags);\r
4813       }\r
4814       SAY("computer starts playing white");\r
4815       break;\r
4816 \r
4817     case IDM_MachineBlack:\r
4818       MachineBlackEvent();\r
4819       /*\r
4820        * refresh the tags dialog only if it's visible\r
4821        */\r
4822       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4823           char *tags;\r
4824           tags = PGNTags(&gameInfo);\r
4825           TagsPopUp(tags, CmailMsg());\r
4826           free(tags);\r
4827       }\r
4828       SAY("computer starts playing black");\r
4829       break;\r
4830 \r
4831     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4832       if(gameMode != BeginningOfGame) { // allow menu item to remain enabled for better mode highligting\r
4833         DisplayError(_("You can only start a match from the initial position."), 0); break;\r
4834       }\r
4835       matchMode = 2;// distinguish from command-line-triggered case (matchMode=1)\r
4836       appData.matchGames = appData.defaultMatchGames;\r
4837       matchGame = 1;\r
4838 \r
4839     case IDM_TwoMachines:\r
4840       TwoMachinesEvent();\r
4841       /*\r
4842        * refresh the tags dialog only if it's visible\r
4843        */\r
4844       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4845           char *tags;\r
4846           tags = PGNTags(&gameInfo);\r
4847           TagsPopUp(tags, CmailMsg());\r
4848           free(tags);\r
4849       }\r
4850       SAY("computer starts playing both sides");\r
4851       break;\r
4852 \r
4853     case IDM_AnalysisMode:\r
4854       if (!first.analysisSupport) {\r
4855         snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4856         DisplayError(buf, 0);\r
4857       } else {\r
4858         SAY("analyzing current position");\r
4859         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4860         if (appData.icsActive) {\r
4861                if (gameMode != IcsObserving) {\r
4862                  snprintf(buf, MSG_SIZ, "You are not observing a game");\r
4863                        DisplayError(buf, 0);\r
4864                        /* secure check */\r
4865                        if (appData.icsEngineAnalyze) {\r
4866                                if (appData.debugMode) \r
4867                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4868                                ExitAnalyzeMode();\r
4869                                ModeHighlight();\r
4870                                break;\r
4871                        }\r
4872                        break;\r
4873                } else {\r
4874                        /* if enable, user want disable icsEngineAnalyze */\r
4875                        if (appData.icsEngineAnalyze) {\r
4876                                ExitAnalyzeMode();\r
4877                                ModeHighlight();\r
4878                                break;\r
4879                        }\r
4880                        appData.icsEngineAnalyze = TRUE;\r
4881                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4882                }\r
4883         } \r
4884         if (!appData.showThinking) ToggleShowThinking();\r
4885         AnalyzeModeEvent();\r
4886       }\r
4887       break;\r
4888 \r
4889     case IDM_AnalyzeFile:\r
4890       if (!first.analysisSupport) {\r
4891         char buf[MSG_SIZ];\r
4892           snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4893         DisplayError(buf, 0);\r
4894       } else {\r
4895         if (!appData.showThinking) ToggleShowThinking();\r
4896         AnalyzeFileEvent();\r
4897         LoadGameDialog(hwnd, _("Analyze Game from File"));\r
4898         AnalysisPeriodicEvent(1);\r
4899       }\r
4900       break;\r
4901 \r
4902     case IDM_IcsClient:\r
4903       IcsClientEvent();\r
4904       break;\r
4905 \r
4906     case IDM_EditGame:\r
4907     case IDM_EditGame2:\r
4908       EditGameEvent();\r
4909       SAY("edit game");\r
4910       break;\r
4911 \r
4912     case IDM_EditPosition:\r
4913     case IDM_EditPosition2:\r
4914       EditPositionEvent();\r
4915       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4916       break;\r
4917 \r
4918     case IDM_Training:\r
4919       TrainingEvent();\r
4920       break;\r
4921 \r
4922     case IDM_ShowGameList:\r
4923       ShowGameListProc();\r
4924       break;\r
4925 \r
4926     case IDM_EditProgs1:\r
4927       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
4928       break;\r
4929 \r
4930     case IDM_EditProgs2:\r
4931       EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);\r
4932       break;\r
4933 \r
4934     case IDM_EditServers:\r
4935       EditTagsPopUp(icsNames, &icsNames);\r
4936       break;\r
4937 \r
4938     case IDM_EditTags:\r
4939     case IDM_Tags:\r
4940       EditTagsProc();\r
4941       break;\r
4942 \r
4943     case IDM_EditComment:\r
4944     case IDM_Comment:\r
4945       if (commentUp && editComment) {\r
4946         CommentPopDown();\r
4947       } else {\r
4948         EditCommentEvent();\r
4949       }\r
4950       break;\r
4951 \r
4952     case IDM_Pause:\r
4953       PauseEvent();\r
4954       break;\r
4955 \r
4956     case IDM_Accept:\r
4957       AcceptEvent();\r
4958       break;\r
4959 \r
4960     case IDM_Decline:\r
4961       DeclineEvent();\r
4962       break;\r
4963 \r
4964     case IDM_Rematch:\r
4965       RematchEvent();\r
4966       break;\r
4967 \r
4968     case IDM_CallFlag:\r
4969       CallFlagEvent();\r
4970       break;\r
4971 \r
4972     case IDM_Draw:\r
4973       DrawEvent();\r
4974       break;\r
4975 \r
4976     case IDM_Adjourn:\r
4977       AdjournEvent();\r
4978       break;\r
4979 \r
4980     case IDM_Abort:\r
4981       AbortEvent();\r
4982       break;\r
4983 \r
4984     case IDM_Resign:\r
4985       ResignEvent();\r
4986       break;\r
4987 \r
4988     case IDM_StopObserving:\r
4989       StopObservingEvent();\r
4990       break;\r
4991 \r
4992     case IDM_StopExamining:\r
4993       StopExaminingEvent();\r
4994       break;\r
4995 \r
4996     case IDM_Upload:\r
4997       UploadGameEvent();\r
4998       break;\r
4999 \r
5000     case IDM_TypeInMove:\r
5001       PopUpMoveDialog('\000');\r
5002       break;\r
5003 \r
5004     case IDM_TypeInName:\r
5005       PopUpNameDialog('\000');\r
5006       break;\r
5007 \r
5008     case IDM_Backward:\r
5009       BackwardEvent();\r
5010       SetFocus(hwndMain);\r
5011       break;\r
5012 \r
5013     JAWS_MENU_ITEMS\r
5014 \r
5015     case IDM_Forward:\r
5016       ForwardEvent();\r
5017       SetFocus(hwndMain);\r
5018       break;\r
5019 \r
5020     case IDM_ToStart:\r
5021       ToStartEvent();\r
5022       SetFocus(hwndMain);\r
5023       break;\r
5024 \r
5025     case IDM_ToEnd:\r
5026       ToEndEvent();\r
5027       SetFocus(hwndMain);\r
5028       break;\r
5029 \r
5030     case IDM_Revert:\r
5031       RevertEvent(FALSE);\r
5032       break;\r
5033 \r
5034     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5035       RevertEvent(TRUE);\r
5036       break;\r
5037 \r
5038     case IDM_TruncateGame:\r
5039       TruncateGameEvent();\r
5040       break;\r
5041 \r
5042     case IDM_MoveNow:\r
5043       MoveNowEvent();\r
5044       break;\r
5045 \r
5046     case IDM_RetractMove:\r
5047       RetractMoveEvent();\r
5048       break;\r
5049 \r
5050     case IDM_FlipView:\r
5051       flipView = !flipView;\r
5052       DrawPosition(FALSE, NULL);\r
5053       break;\r
5054 \r
5055     case IDM_FlipClock:\r
5056       flipClock = !flipClock;\r
5057       DisplayBothClocks();\r
5058       DrawPosition(FALSE, NULL);\r
5059       break;\r
5060 \r
5061     case IDM_MuteSounds:\r
5062       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5063       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5064                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5065       break;\r
5066 \r
5067     case IDM_GeneralOptions:\r
5068       GeneralOptionsPopup(hwnd);\r
5069       DrawPosition(TRUE, NULL);\r
5070       break;\r
5071 \r
5072     case IDM_BoardOptions:\r
5073       BoardOptionsPopup(hwnd);\r
5074       break;\r
5075 \r
5076     case IDM_EnginePlayOptions:\r
5077       EnginePlayOptionsPopup(hwnd);\r
5078       break;\r
5079 \r
5080     case IDM_Engine1Options:\r
5081       EngineOptionsPopup(hwnd, &first);\r
5082       break;\r
5083 \r
5084     case IDM_Engine2Options:\r
5085       savedHwnd = hwnd;\r
5086       if(WaitForSecond(SettingsMenuIfReady)) break;\r
5087       EngineOptionsPopup(hwnd, &second);\r
5088       break;\r
5089 \r
5090     case IDM_OptionsUCI:\r
5091       UciOptionsPopup(hwnd);\r
5092       break;\r
5093 \r
5094     case IDM_IcsOptions:\r
5095       IcsOptionsPopup(hwnd);\r
5096       break;\r
5097 \r
5098     case IDM_Fonts:\r
5099       FontsOptionsPopup(hwnd);\r
5100       break;\r
5101 \r
5102     case IDM_Sounds:\r
5103       SoundOptionsPopup(hwnd);\r
5104       break;\r
5105 \r
5106     case IDM_CommPort:\r
5107       CommPortOptionsPopup(hwnd);\r
5108       break;\r
5109 \r
5110     case IDM_LoadOptions:\r
5111       LoadOptionsPopup(hwnd);\r
5112       break;\r
5113 \r
5114     case IDM_SaveOptions:\r
5115       SaveOptionsPopup(hwnd);\r
5116       break;\r
5117 \r
5118     case IDM_TimeControl:\r
5119       TimeControlOptionsPopup(hwnd);\r
5120       break;\r
5121 \r
5122     case IDM_SaveSettings:\r
5123       SaveSettings(settingsFileName);\r
5124       break;\r
5125 \r
5126     case IDM_SaveSettingsOnExit:\r
5127       saveSettingsOnExit = !saveSettingsOnExit;\r
5128       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5129                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5130                                          MF_CHECKED : MF_UNCHECKED));\r
5131       break;\r
5132 \r
5133     case IDM_Hint:\r
5134       HintEvent();\r
5135       break;\r
5136 \r
5137     case IDM_Book:\r
5138       BookEvent();\r
5139       break;\r
5140 \r
5141     case IDM_AboutGame:\r
5142       AboutGameEvent();\r
5143       break;\r
5144 \r
5145     case IDM_Debug:\r
5146       appData.debugMode = !appData.debugMode;\r
5147       if (appData.debugMode) {\r
5148         char dir[MSG_SIZ];\r
5149         GetCurrentDirectory(MSG_SIZ, dir);\r
5150         SetCurrentDirectory(installDir);\r
5151         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5152         SetCurrentDirectory(dir);\r
5153         setbuf(debugFP, NULL);\r
5154       } else {\r
5155         fclose(debugFP);\r
5156         debugFP = NULL;\r
5157       }\r
5158       break;\r
5159 \r
5160     case IDM_HELPCONTENTS:\r
5161       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5162           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5163           MessageBox (GetFocus(),\r
5164                     _("Unable to activate help"),\r
5165                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5166       }\r
5167       break;\r
5168 \r
5169     case IDM_HELPSEARCH:\r
5170         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5171             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5172         MessageBox (GetFocus(),\r
5173                     _("Unable to activate help"),\r
5174                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5175       }\r
5176       break;\r
5177 \r
5178     case IDM_HELPHELP:\r
5179       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 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_ABOUT:\r
5187       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5188       DialogBox(hInst, \r
5189         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5190         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5191       FreeProcInstance(lpProc);\r
5192       break;\r
5193 \r
5194     case IDM_DirectCommand1:\r
5195       AskQuestionEvent(_("Direct Command"),\r
5196                        _("Send to chess program:"), "", "1");\r
5197       break;\r
5198     case IDM_DirectCommand2:\r
5199       AskQuestionEvent(_("Direct Command"),\r
5200                        _("Send to second chess program:"), "", "2");\r
5201       break;\r
5202 \r
5203     case EP_WhitePawn:\r
5204       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5205       fromX = fromY = -1;\r
5206       break;\r
5207 \r
5208     case EP_WhiteKnight:\r
5209       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5210       fromX = fromY = -1;\r
5211       break;\r
5212 \r
5213     case EP_WhiteBishop:\r
5214       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5215       fromX = fromY = -1;\r
5216       break;\r
5217 \r
5218     case EP_WhiteRook:\r
5219       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5220       fromX = fromY = -1;\r
5221       break;\r
5222 \r
5223     case EP_WhiteQueen:\r
5224       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5225       fromX = fromY = -1;\r
5226       break;\r
5227 \r
5228     case EP_WhiteFerz:\r
5229       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5230       fromX = fromY = -1;\r
5231       break;\r
5232 \r
5233     case EP_WhiteWazir:\r
5234       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5235       fromX = fromY = -1;\r
5236       break;\r
5237 \r
5238     case EP_WhiteAlfil:\r
5239       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5240       fromX = fromY = -1;\r
5241       break;\r
5242 \r
5243     case EP_WhiteCannon:\r
5244       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5245       fromX = fromY = -1;\r
5246       break;\r
5247 \r
5248     case EP_WhiteCardinal:\r
5249       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5250       fromX = fromY = -1;\r
5251       break;\r
5252 \r
5253     case EP_WhiteMarshall:\r
5254       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5255       fromX = fromY = -1;\r
5256       break;\r
5257 \r
5258     case EP_WhiteKing:\r
5259       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5260       fromX = fromY = -1;\r
5261       break;\r
5262 \r
5263     case EP_BlackPawn:\r
5264       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5265       fromX = fromY = -1;\r
5266       break;\r
5267 \r
5268     case EP_BlackKnight:\r
5269       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5270       fromX = fromY = -1;\r
5271       break;\r
5272 \r
5273     case EP_BlackBishop:\r
5274       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5275       fromX = fromY = -1;\r
5276       break;\r
5277 \r
5278     case EP_BlackRook:\r
5279       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5280       fromX = fromY = -1;\r
5281       break;\r
5282 \r
5283     case EP_BlackQueen:\r
5284       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5285       fromX = fromY = -1;\r
5286       break;\r
5287 \r
5288     case EP_BlackFerz:\r
5289       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5290       fromX = fromY = -1;\r
5291       break;\r
5292 \r
5293     case EP_BlackWazir:\r
5294       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5295       fromX = fromY = -1;\r
5296       break;\r
5297 \r
5298     case EP_BlackAlfil:\r
5299       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5300       fromX = fromY = -1;\r
5301       break;\r
5302 \r
5303     case EP_BlackCannon:\r
5304       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5305       fromX = fromY = -1;\r
5306       break;\r
5307 \r
5308     case EP_BlackCardinal:\r
5309       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5310       fromX = fromY = -1;\r
5311       break;\r
5312 \r
5313     case EP_BlackMarshall:\r
5314       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5315       fromX = fromY = -1;\r
5316       break;\r
5317 \r
5318     case EP_BlackKing:\r
5319       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5320       fromX = fromY = -1;\r
5321       break;\r
5322 \r
5323     case EP_EmptySquare:\r
5324       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5325       fromX = fromY = -1;\r
5326       break;\r
5327 \r
5328     case EP_ClearBoard:\r
5329       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5330       fromX = fromY = -1;\r
5331       break;\r
5332 \r
5333     case EP_White:\r
5334       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5335       fromX = fromY = -1;\r
5336       break;\r
5337 \r
5338     case EP_Black:\r
5339       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5340       fromX = fromY = -1;\r
5341       break;\r
5342 \r
5343     case EP_Promote:\r
5344       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5345       fromX = fromY = -1;\r
5346       break;\r
5347 \r
5348     case EP_Demote:\r
5349       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5350       fromX = fromY = -1;\r
5351       break;\r
5352 \r
5353     case DP_Pawn:\r
5354       DropMenuEvent(WhitePawn, fromX, fromY);\r
5355       fromX = fromY = -1;\r
5356       break;\r
5357 \r
5358     case DP_Knight:\r
5359       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5360       fromX = fromY = -1;\r
5361       break;\r
5362 \r
5363     case DP_Bishop:\r
5364       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5365       fromX = fromY = -1;\r
5366       break;\r
5367 \r
5368     case DP_Rook:\r
5369       DropMenuEvent(WhiteRook, fromX, fromY);\r
5370       fromX = fromY = -1;\r
5371       break;\r
5372 \r
5373     case DP_Queen:\r
5374       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5375       fromX = fromY = -1;\r
5376       break;\r
5377 \r
5378     case IDM_English:\r
5379       barbaric = 0;\r
5380       TranslateMenus(0);\r
5381       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5382       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5383       lastChecked = wmId;\r
5384       break;\r
5385 \r
5386     default:\r
5387       if(wmId > IDM_English && wmId < IDM_English+5) {\r
5388           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5389           TranslateMenus(0);\r
5390           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5391           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5392           lastChecked = wmId;\r
5393           break;\r
5394       }\r
5395       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5396     }\r
5397     break;\r
5398 \r
5399   case WM_TIMER:\r
5400     switch (wParam) {\r
5401     case CLOCK_TIMER_ID:\r
5402       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5403       clockTimerEvent = 0;\r
5404       DecrementClocks(); /* call into back end */\r
5405       break;\r
5406     case LOAD_GAME_TIMER_ID:\r
5407       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5408       loadGameTimerEvent = 0;\r
5409       AutoPlayGameLoop(); /* call into back end */\r
5410       break;\r
5411     case ANALYSIS_TIMER_ID:\r
5412       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5413                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5414         AnalysisPeriodicEvent(0);\r
5415       } else {\r
5416         KillTimer(hwnd, analysisTimerEvent);\r
5417         analysisTimerEvent = 0;\r
5418       }\r
5419       break;\r
5420     case DELAYED_TIMER_ID:\r
5421       KillTimer(hwnd, delayedTimerEvent);\r
5422       delayedTimerEvent = 0;\r
5423       delayedTimerCallback();\r
5424       break;\r
5425     }\r
5426     break;\r
5427 \r
5428   case WM_USER_Input:\r
5429     InputEvent(hwnd, message, wParam, lParam);\r
5430     break;\r
5431 \r
5432   /* [AS] Also move "attached" child windows */\r
5433   case WM_WINDOWPOSCHANGING:\r
5434 \r
5435     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5436         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5437 \r
5438         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5439             /* Window is moving */\r
5440             RECT rcMain;\r
5441 \r
5442 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5443             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5444             rcMain.right  = wpMain.x + wpMain.width;\r
5445             rcMain.top    = wpMain.y;\r
5446             rcMain.bottom = wpMain.y + wpMain.height;\r
5447             \r
5448             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5449             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5450             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5451             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5452             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5453             wpMain.x = lpwp->x;\r
5454             wpMain.y = lpwp->y;\r
5455         }\r
5456     }\r
5457     break;\r
5458 \r
5459   /* [AS] Snapping */\r
5460   case WM_ENTERSIZEMOVE:\r
5461     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5462     if (hwnd == hwndMain) {\r
5463       doingSizing = TRUE;\r
5464       lastSizing = 0;\r
5465     }\r
5466     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5467     break;\r
5468 \r
5469   case WM_SIZING:\r
5470     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5471     if (hwnd == hwndMain) {\r
5472       lastSizing = wParam;\r
5473     }\r
5474     break;\r
5475 \r
5476   case WM_MOVING:\r
5477     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5478       return OnMoving( &sd, hwnd, wParam, lParam );\r
5479 \r
5480   case WM_EXITSIZEMOVE:\r
5481     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5482     if (hwnd == hwndMain) {\r
5483       RECT client;\r
5484       doingSizing = FALSE;\r
5485       InvalidateRect(hwnd, &boardRect, FALSE);\r
5486       GetClientRect(hwnd, &client);\r
5487       ResizeBoard(client.right, client.bottom, lastSizing);\r
5488       lastSizing = 0;\r
5489       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5490     }\r
5491     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5492     break;\r
5493 \r
5494   case WM_DESTROY: /* message: window being destroyed */\r
5495     PostQuitMessage(0);\r
5496     break;\r
5497 \r
5498   case WM_CLOSE:\r
5499     if (hwnd == hwndMain) {\r
5500       ExitEvent(0);\r
5501     }\r
5502     break;\r
5503 \r
5504   default:      /* Passes it on if unprocessed */\r
5505     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5506   }\r
5507   return 0;\r
5508 }\r
5509 \r
5510 /*---------------------------------------------------------------------------*\\r
5511  *\r
5512  * Misc utility routines\r
5513  *\r
5514 \*---------------------------------------------------------------------------*/\r
5515 \r
5516 /*\r
5517  * Decent random number generator, at least not as bad as Windows\r
5518  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5519  */\r
5520 unsigned int randstate;\r
5521 \r
5522 int\r
5523 myrandom(void)\r
5524 {\r
5525   randstate = randstate * 1664525 + 1013904223;\r
5526   return (int) randstate & 0x7fffffff;\r
5527 }\r
5528 \r
5529 void\r
5530 mysrandom(unsigned int seed)\r
5531 {\r
5532   randstate = seed;\r
5533 }\r
5534 \r
5535 \r
5536 /* \r
5537  * returns TRUE if user selects a different color, FALSE otherwise \r
5538  */\r
5539 \r
5540 BOOL\r
5541 ChangeColor(HWND hwnd, COLORREF *which)\r
5542 {\r
5543   static BOOL firstTime = TRUE;\r
5544   static DWORD customColors[16];\r
5545   CHOOSECOLOR cc;\r
5546   COLORREF newcolor;\r
5547   int i;\r
5548   ColorClass ccl;\r
5549 \r
5550   if (firstTime) {\r
5551     /* Make initial colors in use available as custom colors */\r
5552     /* Should we put the compiled-in defaults here instead? */\r
5553     i = 0;\r
5554     customColors[i++] = lightSquareColor & 0xffffff;\r
5555     customColors[i++] = darkSquareColor & 0xffffff;\r
5556     customColors[i++] = whitePieceColor & 0xffffff;\r
5557     customColors[i++] = blackPieceColor & 0xffffff;\r
5558     customColors[i++] = highlightSquareColor & 0xffffff;\r
5559     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5560 \r
5561     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5562       customColors[i++] = textAttribs[ccl].color;\r
5563     }\r
5564     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5565     firstTime = FALSE;\r
5566   }\r
5567 \r
5568   cc.lStructSize = sizeof(cc);\r
5569   cc.hwndOwner = hwnd;\r
5570   cc.hInstance = NULL;\r
5571   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5572   cc.lpCustColors = (LPDWORD) customColors;\r
5573   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5574 \r
5575   if (!ChooseColor(&cc)) return FALSE;\r
5576 \r
5577   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5578   if (newcolor == *which) return FALSE;\r
5579   *which = newcolor;\r
5580   return TRUE;\r
5581 \r
5582   /*\r
5583   InitDrawingColors();\r
5584   InvalidateRect(hwnd, &boardRect, FALSE);\r
5585   */\r
5586 }\r
5587 \r
5588 BOOLEAN\r
5589 MyLoadSound(MySound *ms)\r
5590 {\r
5591   BOOL ok = FALSE;\r
5592   struct stat st;\r
5593   FILE *f;\r
5594 \r
5595   if (ms->data) free(ms->data);\r
5596   ms->data = NULL;\r
5597 \r
5598   switch (ms->name[0]) {\r
5599   case NULLCHAR:\r
5600     /* Silence */\r
5601     ok = TRUE;\r
5602     break;\r
5603   case '$':\r
5604     /* System sound from Control Panel.  Don't preload here. */\r
5605     ok = TRUE;\r
5606     break;\r
5607   case '!':\r
5608     if (ms->name[1] == NULLCHAR) {\r
5609       /* "!" alone = silence */\r
5610       ok = TRUE;\r
5611     } else {\r
5612       /* Builtin wave resource.  Error if not found. */\r
5613       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5614       if (h == NULL) break;\r
5615       ms->data = (void *)LoadResource(hInst, h);\r
5616       if (h == NULL) break;\r
5617       ok = TRUE;\r
5618     }\r
5619     break;\r
5620   default:\r
5621     /* .wav file.  Error if not found. */\r
5622     f = fopen(ms->name, "rb");\r
5623     if (f == NULL) break;\r
5624     if (fstat(fileno(f), &st) < 0) break;\r
5625     ms->data = malloc(st.st_size);\r
5626     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5627     fclose(f);\r
5628     ok = TRUE;\r
5629     break;\r
5630   }\r
5631   if (!ok) {\r
5632     char buf[MSG_SIZ];\r
5633       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5634     DisplayError(buf, GetLastError());\r
5635   }\r
5636   return ok;\r
5637 }\r
5638 \r
5639 BOOLEAN\r
5640 MyPlaySound(MySound *ms)\r
5641 {\r
5642   BOOLEAN ok = FALSE;\r
5643 \r
5644   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5645   switch (ms->name[0]) {\r
5646   case NULLCHAR:\r
5647         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5648     /* Silence */\r
5649     ok = TRUE;\r
5650     break;\r
5651   case '$':\r
5652     /* System sound from Control Panel (deprecated feature).\r
5653        "$" alone or an unset sound name gets default beep (still in use). */\r
5654     if (ms->name[1]) {\r
5655       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5656     }\r
5657     if (!ok) ok = MessageBeep(MB_OK);\r
5658     break; \r
5659   case '!':\r
5660     /* Builtin wave resource, or "!" alone for silence */\r
5661     if (ms->name[1]) {\r
5662       if (ms->data == NULL) return FALSE;\r
5663       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5664     } else {\r
5665       ok = TRUE;\r
5666     }\r
5667     break;\r
5668   default:\r
5669     /* .wav file.  Error if not found. */\r
5670     if (ms->data == NULL) return FALSE;\r
5671     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5672     break;\r
5673   }\r
5674   /* Don't print an error: this can happen innocently if the sound driver\r
5675      is busy; for instance, if another instance of WinBoard is playing\r
5676      a sound at about the same time. */\r
5677   return ok;\r
5678 }\r
5679 \r
5680 \r
5681 LRESULT CALLBACK\r
5682 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5683 {\r
5684   BOOL ok;\r
5685   OPENFILENAME *ofn;\r
5686   static UINT *number; /* gross that this is static */\r
5687 \r
5688   switch (message) {\r
5689   case WM_INITDIALOG: /* message: initialize dialog box */\r
5690     /* Center the dialog over the application window */\r
5691     ofn = (OPENFILENAME *) lParam;\r
5692     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5693       number = (UINT *) ofn->lCustData;\r
5694       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5695     } else {\r
5696       number = NULL;\r
5697     }\r
5698     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5699     Translate(hDlg, 1536);\r
5700     return FALSE;  /* Allow for further processing */\r
5701 \r
5702   case WM_COMMAND:\r
5703     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5704       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5705     }\r
5706     return FALSE;  /* Allow for further processing */\r
5707   }\r
5708   return FALSE;\r
5709 }\r
5710 \r
5711 UINT APIENTRY\r
5712 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5713 {\r
5714   static UINT *number;\r
5715   OPENFILENAME *ofname;\r
5716   OFNOTIFY *ofnot;\r
5717   switch (uiMsg) {\r
5718   case WM_INITDIALOG:\r
5719     Translate(hdlg, DLG_IndexNumber);\r
5720     ofname = (OPENFILENAME *)lParam;\r
5721     number = (UINT *)(ofname->lCustData);\r
5722     break;\r
5723   case WM_NOTIFY:\r
5724     ofnot = (OFNOTIFY *)lParam;\r
5725     if (ofnot->hdr.code == CDN_FILEOK) {\r
5726       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5727     }\r
5728     break;\r
5729   }\r
5730   return 0;\r
5731 }\r
5732 \r
5733 \r
5734 FILE *\r
5735 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5736                char *nameFilt, char *dlgTitle, UINT *number,\r
5737                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5738 {\r
5739   OPENFILENAME openFileName;\r
5740   char buf1[MSG_SIZ];\r
5741   FILE *f;\r
5742 \r
5743   if (fileName == NULL) fileName = buf1;\r
5744   if (defName == NULL) {\r
5745     safeStrCpy(fileName, "*.", 3 );\r
5746     strcat(fileName, defExt);\r
5747   } else {\r
5748     safeStrCpy(fileName, defName, MSG_SIZ );\r
5749   }\r
5750     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5751   if (number) *number = 0;\r
5752 \r
5753   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5754   openFileName.hwndOwner         = hwnd;\r
5755   openFileName.hInstance         = (HANDLE) hInst;\r
5756   openFileName.lpstrFilter       = nameFilt;\r
5757   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5758   openFileName.nMaxCustFilter    = 0L;\r
5759   openFileName.nFilterIndex      = 1L;\r
5760   openFileName.lpstrFile         = fileName;\r
5761   openFileName.nMaxFile          = MSG_SIZ;\r
5762   openFileName.lpstrFileTitle    = fileTitle;\r
5763   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5764   openFileName.lpstrInitialDir   = NULL;\r
5765   openFileName.lpstrTitle        = dlgTitle;\r
5766   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5767     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5768     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5769     | (oldDialog ? 0 : OFN_EXPLORER);\r
5770   openFileName.nFileOffset       = 0;\r
5771   openFileName.nFileExtension    = 0;\r
5772   openFileName.lpstrDefExt       = defExt;\r
5773   openFileName.lCustData         = (LONG) number;\r
5774   openFileName.lpfnHook          = oldDialog ?\r
5775     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5776   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5777 \r
5778   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5779                         GetOpenFileName(&openFileName)) {\r
5780     /* open the file */\r
5781     f = fopen(openFileName.lpstrFile, write);\r
5782     if (f == NULL) {\r
5783       MessageBox(hwnd, _("File open failed"), NULL,\r
5784                  MB_OK|MB_ICONEXCLAMATION);\r
5785       return NULL;\r
5786     }\r
5787   } else {\r
5788     int err = CommDlgExtendedError();\r
5789     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5790     return FALSE;\r
5791   }\r
5792   return f;\r
5793 }\r
5794 \r
5795 \r
5796 \r
5797 VOID APIENTRY\r
5798 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5799 {\r
5800   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5801 \r
5802   /*\r
5803    * Get the first pop-up menu in the menu template. This is the\r
5804    * menu that TrackPopupMenu displays.\r
5805    */\r
5806   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5807 \r
5808   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5809 \r
5810   /*\r
5811    * TrackPopup uses screen coordinates, so convert the\r
5812    * coordinates of the mouse click to screen coordinates.\r
5813    */\r
5814   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5815 \r
5816   /* Draw and track the floating pop-up menu. */\r
5817   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5818                  pt.x, pt.y, 0, hwnd, NULL);\r
5819 \r
5820   /* Destroy the menu.*/\r
5821   DestroyMenu(hmenu);\r
5822 }\r
5823    \r
5824 typedef struct {\r
5825   HWND hDlg, hText;\r
5826   int sizeX, sizeY, newSizeX, newSizeY;\r
5827   HDWP hdwp;\r
5828 } ResizeEditPlusButtonsClosure;\r
5829 \r
5830 BOOL CALLBACK\r
5831 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5832 {\r
5833   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5834   RECT rect;\r
5835   POINT pt;\r
5836 \r
5837   if (hChild == cl->hText) return TRUE;\r
5838   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5839   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5840   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5841   ScreenToClient(cl->hDlg, &pt);\r
5842   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5843     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5844   return TRUE;\r
5845 }\r
5846 \r
5847 /* Resize a dialog that has a (rich) edit field filling most of\r
5848    the top, with a row of buttons below */\r
5849 VOID\r
5850 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5851 {\r
5852   RECT rectText;\r
5853   int newTextHeight, newTextWidth;\r
5854   ResizeEditPlusButtonsClosure cl;\r
5855   \r
5856   /*if (IsIconic(hDlg)) return;*/\r
5857   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5858   \r
5859   cl.hdwp = BeginDeferWindowPos(8);\r
5860 \r
5861   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5862   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5863   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5864   if (newTextHeight < 0) {\r
5865     newSizeY += -newTextHeight;\r
5866     newTextHeight = 0;\r
5867   }\r
5868   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5869     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5870 \r
5871   cl.hDlg = hDlg;\r
5872   cl.hText = hText;\r
5873   cl.sizeX = sizeX;\r
5874   cl.sizeY = sizeY;\r
5875   cl.newSizeX = newSizeX;\r
5876   cl.newSizeY = newSizeY;\r
5877   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5878 \r
5879   EndDeferWindowPos(cl.hdwp);\r
5880 }\r
5881 \r
5882 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5883 {\r
5884     RECT    rChild, rParent;\r
5885     int     wChild, hChild, wParent, hParent;\r
5886     int     wScreen, hScreen, xNew, yNew;\r
5887     HDC     hdc;\r
5888 \r
5889     /* Get the Height and Width of the child window */\r
5890     GetWindowRect (hwndChild, &rChild);\r
5891     wChild = rChild.right - rChild.left;\r
5892     hChild = rChild.bottom - rChild.top;\r
5893 \r
5894     /* Get the Height and Width of the parent window */\r
5895     GetWindowRect (hwndParent, &rParent);\r
5896     wParent = rParent.right - rParent.left;\r
5897     hParent = rParent.bottom - rParent.top;\r
5898 \r
5899     /* Get the display limits */\r
5900     hdc = GetDC (hwndChild);\r
5901     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5902     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5903     ReleaseDC(hwndChild, hdc);\r
5904 \r
5905     /* Calculate new X position, then adjust for screen */\r
5906     xNew = rParent.left + ((wParent - wChild) /2);\r
5907     if (xNew < 0) {\r
5908         xNew = 0;\r
5909     } else if ((xNew+wChild) > wScreen) {\r
5910         xNew = wScreen - wChild;\r
5911     }\r
5912 \r
5913     /* Calculate new Y position, then adjust for screen */\r
5914     if( mode == 0 ) {\r
5915         yNew = rParent.top  + ((hParent - hChild) /2);\r
5916     }\r
5917     else {\r
5918         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5919     }\r
5920 \r
5921     if (yNew < 0) {\r
5922         yNew = 0;\r
5923     } else if ((yNew+hChild) > hScreen) {\r
5924         yNew = hScreen - hChild;\r
5925     }\r
5926 \r
5927     /* Set it, and return */\r
5928     return SetWindowPos (hwndChild, NULL,\r
5929                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5930 }\r
5931 \r
5932 /* Center one window over another */\r
5933 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
5934 {\r
5935     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
5936 }\r
5937 \r
5938 /*---------------------------------------------------------------------------*\\r
5939  *\r
5940  * Startup Dialog functions\r
5941  *\r
5942 \*---------------------------------------------------------------------------*/\r
5943 void\r
5944 InitComboStrings(HANDLE hwndCombo, char **cd)\r
5945 {\r
5946   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5947 \r
5948   while (*cd != NULL) {\r
5949     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
5950     cd++;\r
5951   }\r
5952 }\r
5953 \r
5954 void\r
5955 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
5956 {\r
5957   char buf1[MAX_ARG_LEN];\r
5958   int len;\r
5959 \r
5960   if (str[0] == '@') {\r
5961     FILE* f = fopen(str + 1, "r");\r
5962     if (f == NULL) {\r
5963       DisplayFatalError(str + 1, errno, 2);\r
5964       return;\r
5965     }\r
5966     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
5967     fclose(f);\r
5968     buf1[len] = NULLCHAR;\r
5969     str = buf1;\r
5970   }\r
5971 \r
5972   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5973 \r
5974   for (;;) {\r
5975     char buf[MSG_SIZ];\r
5976     char *end = strchr(str, '\n');\r
5977     if (end == NULL) return;\r
5978     memcpy(buf, str, end - str);\r
5979     buf[end - str] = NULLCHAR;\r
5980     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
5981     str = end + 1;\r
5982   }\r
5983 }\r
5984 \r
5985 void\r
5986 SetStartupDialogEnables(HWND hDlg)\r
5987 {\r
5988   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
5989     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5990     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
5991   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
5992     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
5993   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
5994     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
5995   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
5996     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
5997   EnableWindow(GetDlgItem(hDlg, IDOK),\r
5998     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5999     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6000     IsDlgButtonChecked(hDlg, OPT_View));\r
6001 }\r
6002 \r
6003 char *\r
6004 QuoteForFilename(char *filename)\r
6005 {\r
6006   int dquote, space;\r
6007   dquote = strchr(filename, '"') != NULL;\r
6008   space = strchr(filename, ' ') != NULL;\r
6009   if (dquote || space) {\r
6010     if (dquote) {\r
6011       return "'";\r
6012     } else {\r
6013       return "\"";\r
6014     }\r
6015   } else {\r
6016     return "";\r
6017   }\r
6018 }\r
6019 \r
6020 VOID\r
6021 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6022 {\r
6023   char buf[MSG_SIZ];\r
6024   char *q;\r
6025 \r
6026   InitComboStringsFromOption(hwndCombo, nthnames);\r
6027   q = QuoteForFilename(nthcp);\r
6028     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6029   if (*nthdir != NULLCHAR) {\r
6030     q = QuoteForFilename(nthdir);\r
6031       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6032   }\r
6033   if (*nthcp == NULLCHAR) {\r
6034     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6035   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6036     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6037     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6038   }\r
6039 }\r
6040 \r
6041 LRESULT CALLBACK\r
6042 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6043 {\r
6044   char buf[MSG_SIZ];\r
6045   HANDLE hwndCombo;\r
6046   char *p;\r
6047 \r
6048   switch (message) {\r
6049   case WM_INITDIALOG:\r
6050     /* Center the dialog */\r
6051     CenterWindow (hDlg, GetDesktopWindow());\r
6052     Translate(hDlg, DLG_Startup);\r
6053     /* Initialize the dialog items */\r
6054     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6055                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6056                   firstChessProgramNames);\r
6057     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6058                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6059                   secondChessProgramNames);\r
6060     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6061     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6062       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6063     if (*appData.icsHelper != NULLCHAR) {\r
6064       char *q = QuoteForFilename(appData.icsHelper);\r
6065       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6066     }\r
6067     if (*appData.icsHost == NULLCHAR) {\r
6068       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6069       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6070     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6071       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6072       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6073     }\r
6074 \r
6075     if (appData.icsActive) {\r
6076       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6077     }\r
6078     else if (appData.noChessProgram) {\r
6079       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6080     }\r
6081     else {\r
6082       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6083     }\r
6084 \r
6085     SetStartupDialogEnables(hDlg);\r
6086     return TRUE;\r
6087 \r
6088   case WM_COMMAND:\r
6089     switch (LOWORD(wParam)) {\r
6090     case IDOK:\r
6091       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6092         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6093         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6094         p = buf;\r
6095         ParseArgs(StringGet, &p);\r
6096         safeStrCpy(buf, "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6097         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6098         p = buf;\r
6099         ParseArgs(StringGet, &p);\r
6100         appData.noChessProgram = FALSE;\r
6101         appData.icsActive = FALSE;\r
6102       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6103         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6104         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6105         p = buf;\r
6106         ParseArgs(StringGet, &p);\r
6107         if (appData.zippyPlay) {\r
6108           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6109           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6110           p = buf;\r
6111           ParseArgs(StringGet, &p);\r
6112         }\r
6113       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6114         appData.noChessProgram = TRUE;\r
6115         appData.icsActive = FALSE;\r
6116       } else {\r
6117         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6118                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6119         return TRUE;\r
6120       }\r
6121       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6122         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6123         p = buf;\r
6124         ParseArgs(StringGet, &p);\r
6125       }\r
6126       EndDialog(hDlg, TRUE);\r
6127       return TRUE;\r
6128 \r
6129     case IDCANCEL:\r
6130       ExitEvent(0);\r
6131       return TRUE;\r
6132 \r
6133     case IDM_HELPCONTENTS:\r
6134       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6135         MessageBox (GetFocus(),\r
6136                     _("Unable to activate help"),\r
6137                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6138       }\r
6139       break;\r
6140 \r
6141     default:\r
6142       SetStartupDialogEnables(hDlg);\r
6143       break;\r
6144     }\r
6145     break;\r
6146   }\r
6147   return FALSE;\r
6148 }\r
6149 \r
6150 /*---------------------------------------------------------------------------*\\r
6151  *\r
6152  * About box dialog functions\r
6153  *\r
6154 \*---------------------------------------------------------------------------*/\r
6155 \r
6156 /* Process messages for "About" dialog box */\r
6157 LRESULT CALLBACK\r
6158 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6159 {\r
6160   switch (message) {\r
6161   case WM_INITDIALOG: /* message: initialize dialog box */\r
6162     /* Center the dialog over the application window */\r
6163     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6164     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6165     Translate(hDlg, ABOUTBOX);\r
6166     JAWS_COPYRIGHT\r
6167     return (TRUE);\r
6168 \r
6169   case WM_COMMAND: /* message: received a command */\r
6170     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6171         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6172       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6173       return (TRUE);\r
6174     }\r
6175     break;\r
6176   }\r
6177   return (FALSE);\r
6178 }\r
6179 \r
6180 /*---------------------------------------------------------------------------*\\r
6181  *\r
6182  * Comment Dialog functions\r
6183  *\r
6184 \*---------------------------------------------------------------------------*/\r
6185 \r
6186 LRESULT CALLBACK\r
6187 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6188 {\r
6189   static HANDLE hwndText = NULL;\r
6190   int len, newSizeX, newSizeY, flags;\r
6191   static int sizeX, sizeY;\r
6192   char *str;\r
6193   RECT rect;\r
6194   MINMAXINFO *mmi;\r
6195 \r
6196   switch (message) {\r
6197   case WM_INITDIALOG: /* message: initialize dialog box */\r
6198     /* Initialize the dialog items */\r
6199     Translate(hDlg, DLG_EditComment);\r
6200     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6201     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6202     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6203     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6204     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6205     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6206     SetWindowText(hDlg, commentTitle);\r
6207     if (editComment) {\r
6208       SetFocus(hwndText);\r
6209     } else {\r
6210       SetFocus(GetDlgItem(hDlg, IDOK));\r
6211     }\r
6212     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6213                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6214                 MAKELPARAM(FALSE, 0));\r
6215     /* Size and position the dialog */\r
6216     if (!commentDialog) {\r
6217       commentDialog = hDlg;\r
6218       flags = SWP_NOZORDER;\r
6219       GetClientRect(hDlg, &rect);\r
6220       sizeX = rect.right;\r
6221       sizeY = rect.bottom;\r
6222       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6223           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6224         WINDOWPLACEMENT wp;\r
6225         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6226         wp.length = sizeof(WINDOWPLACEMENT);\r
6227         wp.flags = 0;\r
6228         wp.showCmd = SW_SHOW;\r
6229         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6230         wp.rcNormalPosition.left = wpComment.x;\r
6231         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6232         wp.rcNormalPosition.top = wpComment.y;\r
6233         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6234         SetWindowPlacement(hDlg, &wp);\r
6235 \r
6236         GetClientRect(hDlg, &rect);\r
6237         newSizeX = rect.right;\r
6238         newSizeY = rect.bottom;\r
6239         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6240                               newSizeX, newSizeY);\r
6241         sizeX = newSizeX;\r
6242         sizeY = newSizeY;\r
6243       }\r
6244     }\r
6245     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );\r
6246     return FALSE;\r
6247 \r
6248   case WM_COMMAND: /* message: received a command */\r
6249     switch (LOWORD(wParam)) {\r
6250     case IDOK:\r
6251       if (editComment) {\r
6252         char *p, *q;\r
6253         /* Read changed options from the dialog box */\r
6254         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6255         len = GetWindowTextLength(hwndText);\r
6256         str = (char *) malloc(len + 1);\r
6257         GetWindowText(hwndText, str, len + 1);\r
6258         p = q = str;\r
6259         while (*q) {\r
6260           if (*q == '\r')\r
6261             q++;\r
6262           else\r
6263             *p++ = *q++;\r
6264         }\r
6265         *p = NULLCHAR;\r
6266         ReplaceComment(commentIndex, str);\r
6267         free(str);\r
6268       }\r
6269       CommentPopDown();\r
6270       return TRUE;\r
6271 \r
6272     case IDCANCEL:\r
6273     case OPT_CancelComment:\r
6274       CommentPopDown();\r
6275       return TRUE;\r
6276 \r
6277     case OPT_ClearComment:\r
6278       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6279       break;\r
6280 \r
6281     case OPT_EditComment:\r
6282       EditCommentEvent();\r
6283       return TRUE;\r
6284 \r
6285     default:\r
6286       break;\r
6287     }\r
6288     break;\r
6289 \r
6290   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6291         if( wParam == OPT_CommentText ) {\r
6292             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6293 \r
6294             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) {\r
6295                 POINTL pt;\r
6296                 LRESULT index;\r
6297 \r
6298                 pt.x = LOWORD( lpMF->lParam );\r
6299                 pt.y = HIWORD( lpMF->lParam );\r
6300 \r
6301                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6302 \r
6303                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6304                 len = GetWindowTextLength(hwndText);\r
6305                 str = (char *) malloc(len + 1);\r
6306                 GetWindowText(hwndText, str, len + 1);\r
6307                 ReplaceComment(commentIndex, str);\r
6308                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6309                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6310                 free(str);\r
6311 \r
6312                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6313                 lpMF->msg = WM_USER;\r
6314 \r
6315                 return TRUE;\r
6316             }\r
6317         }\r
6318         break;\r
6319 \r
6320   case WM_SIZE:\r
6321     newSizeX = LOWORD(lParam);\r
6322     newSizeY = HIWORD(lParam);\r
6323     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6324     sizeX = newSizeX;\r
6325     sizeY = newSizeY;\r
6326     break;\r
6327 \r
6328   case WM_GETMINMAXINFO:\r
6329     /* Prevent resizing window too small */\r
6330     mmi = (MINMAXINFO *) lParam;\r
6331     mmi->ptMinTrackSize.x = 100;\r
6332     mmi->ptMinTrackSize.y = 100;\r
6333     break;\r
6334   }\r
6335   return FALSE;\r
6336 }\r
6337 \r
6338 VOID\r
6339 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6340 {\r
6341   FARPROC lpProc;\r
6342   char *p, *q;\r
6343 \r
6344   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6345 \r
6346   if (str == NULL) str = "";\r
6347   p = (char *) malloc(2 * strlen(str) + 2);\r
6348   q = p;\r
6349   while (*str) {\r
6350     if (*str == '\n') *q++ = '\r';\r
6351     *q++ = *str++;\r
6352   }\r
6353   *q = NULLCHAR;\r
6354   if (commentText != NULL) free(commentText);\r
6355 \r
6356   commentIndex = index;\r
6357   commentTitle = title;\r
6358   commentText = p;\r
6359   editComment = edit;\r
6360 \r
6361   if (commentDialog) {\r
6362     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6363     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6364   } else {\r
6365     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6366     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6367                  hwndMain, (DLGPROC)lpProc);\r
6368     FreeProcInstance(lpProc);\r
6369   }\r
6370   commentUp = TRUE;\r
6371 }\r
6372 \r
6373 \r
6374 /*---------------------------------------------------------------------------*\\r
6375  *\r
6376  * Type-in move dialog functions\r
6377  * \r
6378 \*---------------------------------------------------------------------------*/\r
6379 \r
6380 LRESULT CALLBACK\r
6381 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6382 {\r
6383   char move[MSG_SIZ];\r
6384   HWND hInput;\r
6385   ChessMove moveType;\r
6386   int fromX, fromY, toX, toY;\r
6387   char promoChar;\r
6388 \r
6389   switch (message) {\r
6390   case WM_INITDIALOG:\r
6391     move[0] = (char) lParam;\r
6392     move[1] = NULLCHAR;\r
6393     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6394     Translate(hDlg, DLG_TypeInMove);\r
6395     hInput = GetDlgItem(hDlg, OPT_Move);\r
6396     SetWindowText(hInput, move);\r
6397     SetFocus(hInput);\r
6398     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6399     return FALSE;\r
6400 \r
6401   case WM_COMMAND:\r
6402     switch (LOWORD(wParam)) {\r
6403     case IDOK:
6404       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6405       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6406       { int n; Board board;\r
6407         // [HGM] FENedit\r
6408         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
6409                 EditPositionPasteFEN(move);\r
6410                 EndDialog(hDlg, TRUE);\r
6411                 return TRUE;\r
6412         }\r
6413         // [HGM] movenum: allow move number to be typed in any mode\r
6414         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
6415           ToNrEvent(2*n-1);\r
6416           EndDialog(hDlg, TRUE);\r
6417           return TRUE;\r
6418         }\r
6419       }\r
6420       if (gameMode != EditGame && currentMove != forwardMostMove && \r
6421         gameMode != Training) {\r
6422         DisplayMoveError(_("Displayed move is not current"));\r
6423       } else {\r
6424 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
6425         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6426           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
6427         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
6428         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6429           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
6430           if (gameMode != Training)\r
6431               forwardMostMove = currentMove;\r
6432           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
6433         } else {\r
6434           DisplayMoveError(_("Could not parse move"));\r
6435         }\r
6436       }\r
6437       EndDialog(hDlg, TRUE);\r
6438       return TRUE;\r
6439     case IDCANCEL:\r
6440       EndDialog(hDlg, FALSE);\r
6441       return TRUE;\r
6442     default:\r
6443       break;\r
6444     }\r
6445     break;\r
6446   }\r
6447   return FALSE;\r
6448 }\r
6449 \r
6450 VOID\r
6451 PopUpMoveDialog(char firstchar)\r
6452 {\r
6453     FARPROC lpProc;\r
6454     \r
6455     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
6456         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
6457         gameMode == AnalyzeMode || gameMode == EditGame || \r
6458         gameMode == EditPosition || gameMode == IcsExamining ||\r
6459         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
6460         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
6461                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
6462                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
6463         gameMode == Training) {\r
6464       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6465       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6466         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6467       FreeProcInstance(lpProc);\r
6468     }\r
6469 }\r
6470 \r
6471 /*---------------------------------------------------------------------------*\\r
6472  *\r
6473  * Type-in name dialog functions\r
6474  * \r
6475 \*---------------------------------------------------------------------------*/\r
6476 \r
6477 LRESULT CALLBACK\r
6478 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6479 {\r
6480   char move[MSG_SIZ];\r
6481   HWND hInput;\r
6482 \r
6483   switch (message) {\r
6484   case WM_INITDIALOG:\r
6485     move[0] = (char) lParam;\r
6486     move[1] = NULLCHAR;\r
6487     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6488     Translate(hDlg, DLG_TypeInName);\r
6489     hInput = GetDlgItem(hDlg, OPT_Name);\r
6490     SetWindowText(hInput, move);\r
6491     SetFocus(hInput);\r
6492     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6493     return FALSE;\r
6494 \r
6495   case WM_COMMAND:\r
6496     switch (LOWORD(wParam)) {\r
6497     case IDOK:\r
6498       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6499       appData.userName = strdup(move);\r
6500       SetUserLogo();\r
6501       SetGameInfo();\r
6502       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6503         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6504         DisplayTitle(move);\r
6505       }\r
6506 \r
6507 \r
6508       EndDialog(hDlg, TRUE);\r
6509       return TRUE;\r
6510     case IDCANCEL:\r
6511       EndDialog(hDlg, FALSE);\r
6512       return TRUE;\r
6513     default:\r
6514       break;\r
6515     }\r
6516     break;\r
6517   }\r
6518   return FALSE;\r
6519 }\r
6520 \r
6521 VOID\r
6522 PopUpNameDialog(char firstchar)\r
6523 {\r
6524     FARPROC lpProc;\r
6525     \r
6526       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6527       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6528         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6529       FreeProcInstance(lpProc);\r
6530 }\r
6531 \r
6532 /*---------------------------------------------------------------------------*\\r
6533  *\r
6534  *  Error dialogs\r
6535  * \r
6536 \*---------------------------------------------------------------------------*/\r
6537 \r
6538 /* Nonmodal error box */\r
6539 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6540                              WPARAM wParam, LPARAM lParam);\r
6541 \r
6542 VOID\r
6543 ErrorPopUp(char *title, char *content)\r
6544 {\r
6545   FARPROC lpProc;\r
6546   char *p, *q;\r
6547   BOOLEAN modal = hwndMain == NULL;\r
6548 \r
6549   p = content;\r
6550   q = errorMessage;\r
6551   while (*p) {\r
6552     if (*p == '\n') {\r
6553       if (modal) {\r
6554         *q++ = ' ';\r
6555         p++;\r
6556       } else {\r
6557         *q++ = '\r';\r
6558         *q++ = *p++;\r
6559       }\r
6560     } else {\r
6561       *q++ = *p++;\r
6562     }\r
6563   }\r
6564   *q = NULLCHAR;\r
6565   strncpy(errorTitle, title, sizeof(errorTitle));\r
6566   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6567   \r
6568   if (modal) {\r
6569     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6570   } else {\r
6571     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6572     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6573                  hwndMain, (DLGPROC)lpProc);\r
6574     FreeProcInstance(lpProc);\r
6575   }\r
6576 }\r
6577 \r
6578 VOID\r
6579 ErrorPopDown()\r
6580 {\r
6581   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6582   if (errorDialog == NULL) return;\r
6583   DestroyWindow(errorDialog);\r
6584   errorDialog = NULL;\r
6585   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6586 }\r
6587 \r
6588 LRESULT CALLBACK\r
6589 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6590 {\r
6591   HANDLE hwndText;\r
6592   RECT rChild;\r
6593 \r
6594   switch (message) {\r
6595   case WM_INITDIALOG:\r
6596     GetWindowRect(hDlg, &rChild);\r
6597 \r
6598     /*\r
6599     SetWindowPos(hDlg, NULL, rChild.left,\r
6600       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6601       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6602     */\r
6603 \r
6604     /* \r
6605         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6606         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6607         and it doesn't work when you resize the dialog.\r
6608         For now, just give it a default position.\r
6609     */\r
6610     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6611     Translate(hDlg, DLG_Error);\r
6612 \r
6613     errorDialog = hDlg;\r
6614     SetWindowText(hDlg, errorTitle);\r
6615     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6616     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6617     return FALSE;\r
6618 \r
6619   case WM_COMMAND:\r
6620     switch (LOWORD(wParam)) {\r
6621     case IDOK:\r
6622     case IDCANCEL:\r
6623       if (errorDialog == hDlg) errorDialog = NULL;\r
6624       DestroyWindow(hDlg);\r
6625       return TRUE;\r
6626 \r
6627     default:\r
6628       break;\r
6629     }\r
6630     break;\r
6631   }\r
6632   return FALSE;\r
6633 }\r
6634 \r
6635 #ifdef GOTHIC\r
6636 HWND gothicDialog = NULL;\r
6637 \r
6638 LRESULT CALLBACK\r
6639 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6640 {\r
6641   HANDLE hwndText;\r
6642   RECT rChild;\r
6643   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6644 \r
6645   switch (message) {\r
6646   case WM_INITDIALOG:\r
6647     GetWindowRect(hDlg, &rChild);\r
6648 \r
6649     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6650                                                              SWP_NOZORDER);\r
6651 \r
6652     /* \r
6653         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6654         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6655         and it doesn't work when you resize the dialog.\r
6656         For now, just give it a default position.\r
6657     */\r
6658     gothicDialog = hDlg;\r
6659     SetWindowText(hDlg, errorTitle);\r
6660     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6661     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6662     return FALSE;\r
6663 \r
6664   case WM_COMMAND:\r
6665     switch (LOWORD(wParam)) {\r
6666     case IDOK:\r
6667     case IDCANCEL:\r
6668       if (errorDialog == hDlg) errorDialog = NULL;\r
6669       DestroyWindow(hDlg);\r
6670       return TRUE;\r
6671 \r
6672     default:\r
6673       break;\r
6674     }\r
6675     break;\r
6676   }\r
6677   return FALSE;\r
6678 }\r
6679 \r
6680 VOID\r
6681 GothicPopUp(char *title, VariantClass variant)\r
6682 {\r
6683   FARPROC lpProc;\r
6684   static char *lastTitle;\r
6685 \r
6686   strncpy(errorTitle, title, sizeof(errorTitle));\r
6687   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6688 \r
6689   if(lastTitle != title && gothicDialog != NULL) {\r
6690     DestroyWindow(gothicDialog);\r
6691     gothicDialog = NULL;\r
6692   }\r
6693   if(variant != VariantNormal && gothicDialog == NULL) {\r
6694     title = lastTitle;\r
6695     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6696     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6697                  hwndMain, (DLGPROC)lpProc);\r
6698     FreeProcInstance(lpProc);\r
6699   }\r
6700 }\r
6701 #endif\r
6702 \r
6703 /*---------------------------------------------------------------------------*\\r
6704  *\r
6705  *  Ics Interaction console functions\r
6706  *\r
6707 \*---------------------------------------------------------------------------*/\r
6708 \r
6709 #define HISTORY_SIZE 64\r
6710 static char *history[HISTORY_SIZE];\r
6711 int histIn = 0, histP = 0;\r
6712 \r
6713 VOID\r
6714 SaveInHistory(char *cmd)\r
6715 {\r
6716   if (history[histIn] != NULL) {\r
6717     free(history[histIn]);\r
6718     history[histIn] = NULL;\r
6719   }\r
6720   if (*cmd == NULLCHAR) return;\r
6721   history[histIn] = StrSave(cmd);\r
6722   histIn = (histIn + 1) % HISTORY_SIZE;\r
6723   if (history[histIn] != NULL) {\r
6724     free(history[histIn]);\r
6725     history[histIn] = NULL;\r
6726   }\r
6727   histP = histIn;\r
6728 }\r
6729 \r
6730 char *\r
6731 PrevInHistory(char *cmd)\r
6732 {\r
6733   int newhp;\r
6734   if (histP == histIn) {\r
6735     if (history[histIn] != NULL) free(history[histIn]);\r
6736     history[histIn] = StrSave(cmd);\r
6737   }\r
6738   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6739   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6740   histP = newhp;\r
6741   return history[histP];\r
6742 }\r
6743 \r
6744 char *\r
6745 NextInHistory()\r
6746 {\r
6747   if (histP == histIn) return NULL;\r
6748   histP = (histP + 1) % HISTORY_SIZE;\r
6749   return history[histP];   \r
6750 }\r
6751 \r
6752 HMENU\r
6753 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6754 {\r
6755   HMENU hmenu, h;\r
6756   int i = 0;\r
6757   hmenu = LoadMenu(hInst, "TextMenu");\r
6758   h = GetSubMenu(hmenu, 0);\r
6759   while (e->item) {\r
6760     if (strcmp(e->item, "-") == 0) {\r
6761       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6762     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6763       int flags = MF_STRING, j = 0;\r
6764       if (e->item[0] == '|') {\r
6765         flags |= MF_MENUBARBREAK;\r
6766         j++;\r
6767       }\r
6768       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6769       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6770     }\r
6771     e++;\r
6772     i++;\r
6773   } \r
6774   return hmenu;\r
6775 }\r
6776 \r
6777 WNDPROC consoleTextWindowProc;\r
6778 \r
6779 void\r
6780 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6781 {\r
6782   char buf[MSG_SIZ], name[MSG_SIZ];\r
6783   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6784   CHARRANGE sel;\r
6785 \r
6786   if (!getname) {\r
6787     SetWindowText(hInput, command);\r
6788     if (immediate) {\r
6789       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6790     } else {\r
6791       sel.cpMin = 999999;\r
6792       sel.cpMax = 999999;\r
6793       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6794       SetFocus(hInput);\r
6795     }\r
6796     return;\r
6797   }    \r
6798   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6799   if (sel.cpMin == sel.cpMax) {\r
6800     /* Expand to surrounding word */\r
6801     TEXTRANGE tr;\r
6802     do {\r
6803       tr.chrg.cpMax = sel.cpMin;\r
6804       tr.chrg.cpMin = --sel.cpMin;\r
6805       if (sel.cpMin < 0) break;\r
6806       tr.lpstrText = name;\r
6807       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6808     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6809     sel.cpMin++;\r
6810 \r
6811     do {\r
6812       tr.chrg.cpMin = sel.cpMax;\r
6813       tr.chrg.cpMax = ++sel.cpMax;\r
6814       tr.lpstrText = name;\r
6815       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6816     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6817     sel.cpMax--;\r
6818 \r
6819     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6820       MessageBeep(MB_ICONEXCLAMATION);\r
6821       return;\r
6822     }\r
6823     tr.chrg = sel;\r
6824     tr.lpstrText = name;\r
6825     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6826   } else {\r
6827     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6828       MessageBeep(MB_ICONEXCLAMATION);\r
6829       return;\r
6830     }\r
6831     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6832   }\r
6833   if (immediate) {\r
6834     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6835     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6836     SetWindowText(hInput, buf);\r
6837     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6838   } else {\r
6839     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6840       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6841     SetWindowText(hInput, buf);\r
6842     sel.cpMin = 999999;\r
6843     sel.cpMax = 999999;\r
6844     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6845     SetFocus(hInput);\r
6846   }\r
6847 }\r
6848 \r
6849 LRESULT CALLBACK \r
6850 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6851 {\r
6852   HWND hInput;\r
6853   CHARRANGE sel;\r
6854 \r
6855   switch (message) {\r
6856   case WM_KEYDOWN:\r
6857     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6858     switch (wParam) {\r
6859     case VK_PRIOR:\r
6860       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6861       return 0;\r
6862     case VK_NEXT:\r
6863       sel.cpMin = 999999;\r
6864       sel.cpMax = 999999;\r
6865       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6866       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6867       return 0;\r
6868     }\r
6869     break;\r
6870   case WM_CHAR:\r
6871    if(wParam != '\022') {\r
6872     if (wParam == '\t') {\r
6873       if (GetKeyState(VK_SHIFT) < 0) {\r
6874         /* shifted */\r
6875         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6876         if (buttonDesc[0].hwnd) {\r
6877           SetFocus(buttonDesc[0].hwnd);\r
6878         } else {\r
6879           SetFocus(hwndMain);\r
6880         }\r
6881       } else {\r
6882         /* unshifted */\r
6883         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6884       }\r
6885     } else {\r
6886       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6887       JAWS_DELETE( SetFocus(hInput); )\r
6888       SendMessage(hInput, message, wParam, lParam);\r
6889     }\r
6890     return 0;\r
6891    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
6892   case WM_RBUTTONDOWN:\r
6893     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6894       /* Move selection here if it was empty */\r
6895       POINT pt;\r
6896       pt.x = LOWORD(lParam);\r
6897       pt.y = HIWORD(lParam);\r
6898       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6899       if (sel.cpMin == sel.cpMax) {\r
6900         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6901         sel.cpMax = sel.cpMin;\r
6902         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6903       }\r
6904       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6905 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6906       POINT pt;\r
6907       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6908       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6909       if (sel.cpMin == sel.cpMax) {\r
6910         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6911         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6912       }\r
6913       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6914         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6915       }\r
6916       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6917       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6918       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6919       MenuPopup(hwnd, pt, hmenu, -1);\r
6920 }\r
6921     }\r
6922     return 0;\r
6923   case WM_RBUTTONUP:\r
6924     if (GetKeyState(VK_SHIFT) & ~1) {\r
6925       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6926         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6927     }\r
6928     return 0;\r
6929   case WM_PASTE:\r
6930     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6931     SetFocus(hInput);\r
6932     return SendMessage(hInput, message, wParam, lParam);\r
6933   case WM_MBUTTONDOWN:\r
6934     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6935   case WM_COMMAND:\r
6936     switch (LOWORD(wParam)) {\r
6937     case IDM_QuickPaste:\r
6938       {\r
6939         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6940         if (sel.cpMin == sel.cpMax) {\r
6941           MessageBeep(MB_ICONEXCLAMATION);\r
6942           return 0;\r
6943         }\r
6944         SendMessage(hwnd, WM_COPY, 0, 0);\r
6945         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6946         SendMessage(hInput, WM_PASTE, 0, 0);\r
6947         SetFocus(hInput);\r
6948         return 0;\r
6949       }\r
6950     case IDM_Cut:\r
6951       SendMessage(hwnd, WM_CUT, 0, 0);\r
6952       return 0;\r
6953     case IDM_Paste:\r
6954       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6955       return 0;\r
6956     case IDM_Copy:\r
6957       SendMessage(hwnd, WM_COPY, 0, 0);\r
6958       return 0;\r
6959     default:\r
6960       {\r
6961         int i = LOWORD(wParam) - IDM_CommandX;\r
6962         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6963             icsTextMenuEntry[i].command != NULL) {\r
6964           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6965                    icsTextMenuEntry[i].getname,\r
6966                    icsTextMenuEntry[i].immediate);\r
6967           return 0;\r
6968         }\r
6969       }\r
6970       break;\r
6971     }\r
6972     break;\r
6973   }\r
6974   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
6975 }\r
6976 \r
6977 WNDPROC consoleInputWindowProc;\r
6978 \r
6979 LRESULT CALLBACK\r
6980 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6981 {\r
6982   char buf[MSG_SIZ];\r
6983   char *p;\r
6984   static BOOL sendNextChar = FALSE;\r
6985   static BOOL quoteNextChar = FALSE;\r
6986   InputSource *is = consoleInputSource;\r
6987   CHARFORMAT cf;\r
6988   CHARRANGE sel;\r
6989 \r
6990   switch (message) {\r
6991   case WM_CHAR:\r
6992     if (!appData.localLineEditing || sendNextChar) {\r
6993       is->buf[0] = (CHAR) wParam;\r
6994       is->count = 1;\r
6995       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6996       sendNextChar = FALSE;\r
6997       return 0;\r
6998     }\r
6999     if (quoteNextChar) {\r
7000       buf[0] = (char) wParam;\r
7001       buf[1] = NULLCHAR;\r
7002       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7003       quoteNextChar = FALSE;\r
7004       return 0;\r
7005     }\r
7006     switch (wParam) {\r
7007     case '\r':   /* Enter key */\r
7008       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7009       if (consoleEcho) SaveInHistory(is->buf);\r
7010       is->buf[is->count++] = '\n';\r
7011       is->buf[is->count] = NULLCHAR;\r
7012       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7013       if (consoleEcho) {\r
7014         ConsoleOutput(is->buf, is->count, TRUE);\r
7015       } else if (appData.localLineEditing) {\r
7016         ConsoleOutput("\n", 1, TRUE);\r
7017       }\r
7018       /* fall thru */\r
7019     case '\033': /* Escape key */\r
7020       SetWindowText(hwnd, "");\r
7021       cf.cbSize = sizeof(CHARFORMAT);\r
7022       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7023       if (consoleEcho) {\r
7024         cf.crTextColor = textAttribs[ColorNormal].color;\r
7025       } else {\r
7026         cf.crTextColor = COLOR_ECHOOFF;\r
7027       }\r
7028       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7029       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7030       return 0;\r
7031     case '\t':   /* Tab key */\r
7032       if (GetKeyState(VK_SHIFT) < 0) {\r
7033         /* shifted */\r
7034         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7035       } else {\r
7036         /* unshifted */\r
7037         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7038         if (buttonDesc[0].hwnd) {\r
7039           SetFocus(buttonDesc[0].hwnd);\r
7040         } else {\r
7041           SetFocus(hwndMain);\r
7042         }\r
7043       }\r
7044       return 0;\r
7045     case '\023': /* Ctrl+S */\r
7046       sendNextChar = TRUE;\r
7047       return 0;\r
7048     case '\021': /* Ctrl+Q */\r
7049       quoteNextChar = TRUE;\r
7050       return 0;\r
7051     JAWS_REPLAY\r
7052     default:\r
7053       break;\r
7054     }\r
7055     break;\r
7056   case WM_KEYDOWN:\r
7057     switch (wParam) {\r
7058     case VK_UP:\r
7059       GetWindowText(hwnd, buf, MSG_SIZ);\r
7060       p = PrevInHistory(buf);\r
7061       if (p != NULL) {\r
7062         SetWindowText(hwnd, p);\r
7063         sel.cpMin = 999999;\r
7064         sel.cpMax = 999999;\r
7065         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7066         return 0;\r
7067       }\r
7068       break;\r
7069     case VK_DOWN:\r
7070       p = NextInHistory();\r
7071       if (p != NULL) {\r
7072         SetWindowText(hwnd, p);\r
7073         sel.cpMin = 999999;\r
7074         sel.cpMax = 999999;\r
7075         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7076         return 0;\r
7077       }\r
7078       break;\r
7079     case VK_HOME:\r
7080     case VK_END:\r
7081       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7082       /* fall thru */\r
7083     case VK_PRIOR:\r
7084     case VK_NEXT:\r
7085       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7086       return 0;\r
7087     }\r
7088     break;\r
7089   case WM_MBUTTONDOWN:\r
7090     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7091       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7092     break;\r
7093   case WM_RBUTTONUP:\r
7094     if (GetKeyState(VK_SHIFT) & ~1) {\r
7095       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7096         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7097     } else {\r
7098       POINT pt;\r
7099       HMENU hmenu;\r
7100       hmenu = LoadMenu(hInst, "InputMenu");\r
7101       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7102       if (sel.cpMin == sel.cpMax) {\r
7103         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7104         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7105       }\r
7106       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7107         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7108       }\r
7109       pt.x = LOWORD(lParam);\r
7110       pt.y = HIWORD(lParam);\r
7111       MenuPopup(hwnd, pt, hmenu, -1);\r
7112     }\r
7113     return 0;\r
7114   case WM_COMMAND:\r
7115     switch (LOWORD(wParam)) { \r
7116     case IDM_Undo:\r
7117       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7118       return 0;\r
7119     case IDM_SelectAll:\r
7120       sel.cpMin = 0;\r
7121       sel.cpMax = -1; /*999999?*/\r
7122       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7123       return 0;\r
7124     case IDM_Cut:\r
7125       SendMessage(hwnd, WM_CUT, 0, 0);\r
7126       return 0;\r
7127     case IDM_Paste:\r
7128       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7129       return 0;\r
7130     case IDM_Copy:\r
7131       SendMessage(hwnd, WM_COPY, 0, 0);\r
7132       return 0;\r
7133     }\r
7134     break;\r
7135   }\r
7136   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7137 }\r
7138 \r
7139 #define CO_MAX  100000\r
7140 #define CO_TRIM   1000\r
7141 \r
7142 LRESULT CALLBACK\r
7143 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7144 {\r
7145   static SnapData sd;\r
7146   HWND hText, hInput;\r
7147   RECT rect;\r
7148   static int sizeX, sizeY;\r
7149   int newSizeX, newSizeY;\r
7150   MINMAXINFO *mmi;\r
7151   WORD wMask;\r
7152 \r
7153   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7154   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7155 \r
7156   switch (message) {\r
7157   case WM_NOTIFY:\r
7158     if (((NMHDR*)lParam)->code == EN_LINK)\r
7159     {\r
7160       ENLINK *pLink = (ENLINK*)lParam;\r
7161       if (pLink->msg == WM_LBUTTONUP)\r
7162       {\r
7163         TEXTRANGE tr;\r
7164 \r
7165         tr.chrg = pLink->chrg;\r
7166         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7167         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7168         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7169         free(tr.lpstrText);\r
7170       }\r
7171     }\r
7172     break;\r
7173   case WM_INITDIALOG: /* message: initialize dialog box */\r
7174     hwndConsole = hDlg;\r
7175     SetFocus(hInput);\r
7176     consoleTextWindowProc = (WNDPROC)\r
7177       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
7178     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7179     consoleInputWindowProc = (WNDPROC)\r
7180       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
7181     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7182     Colorize(ColorNormal, TRUE);\r
7183     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7184     ChangedConsoleFont();\r
7185     GetClientRect(hDlg, &rect);\r
7186     sizeX = rect.right;\r
7187     sizeY = rect.bottom;\r
7188     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7189         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7190       WINDOWPLACEMENT wp;\r
7191       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7192       wp.length = sizeof(WINDOWPLACEMENT);\r
7193       wp.flags = 0;\r
7194       wp.showCmd = SW_SHOW;\r
7195       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7196       wp.rcNormalPosition.left = wpConsole.x;\r
7197       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7198       wp.rcNormalPosition.top = wpConsole.y;\r
7199       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7200       SetWindowPlacement(hDlg, &wp);\r
7201     }\r
7202 \r
7203    // [HGM] Chessknight's change 2004-07-13\r
7204    else { /* Determine Defaults */\r
7205        WINDOWPLACEMENT wp;\r
7206        wpConsole.x = wpMain.width + 1;\r
7207        wpConsole.y = wpMain.y;\r
7208        wpConsole.width = screenWidth -  wpMain.width;\r
7209        wpConsole.height = wpMain.height;\r
7210        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7211        wp.length = sizeof(WINDOWPLACEMENT);\r
7212        wp.flags = 0;\r
7213        wp.showCmd = SW_SHOW;\r
7214        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7215        wp.rcNormalPosition.left = wpConsole.x;\r
7216        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7217        wp.rcNormalPosition.top = wpConsole.y;\r
7218        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7219        SetWindowPlacement(hDlg, &wp);\r
7220     }\r
7221 \r
7222    // Allow hText to highlight URLs and send notifications on them\r
7223    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7224    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7225    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7226    SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width\r
7227 \r
7228     return FALSE;\r
7229 \r
7230   case WM_SETFOCUS:\r
7231     SetFocus(hInput);\r
7232     return 0;\r
7233 \r
7234   case WM_CLOSE:\r
7235     ExitEvent(0);\r
7236     /* not reached */\r
7237     break;\r
7238 \r
7239   case WM_SIZE:\r
7240     if (IsIconic(hDlg)) break;\r
7241     newSizeX = LOWORD(lParam);\r
7242     newSizeY = HIWORD(lParam);\r
7243     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7244       RECT rectText, rectInput;\r
7245       POINT pt;\r
7246       int newTextHeight, newTextWidth;\r
7247       GetWindowRect(hText, &rectText);\r
7248       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7249       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7250       if (newTextHeight < 0) {\r
7251         newSizeY += -newTextHeight;\r
7252         newTextHeight = 0;\r
7253       }\r
7254       SetWindowPos(hText, NULL, 0, 0,\r
7255         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7256       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7257       pt.x = rectInput.left;\r
7258       pt.y = rectInput.top + newSizeY - sizeY;\r
7259       ScreenToClient(hDlg, &pt);\r
7260       SetWindowPos(hInput, NULL, \r
7261         pt.x, pt.y, /* needs client coords */   \r
7262         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7263         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7264     }\r
7265     sizeX = newSizeX;\r
7266     sizeY = newSizeY;\r
7267     break;\r
7268 \r
7269   case WM_GETMINMAXINFO:\r
7270     /* Prevent resizing window too small */\r
7271     mmi = (MINMAXINFO *) lParam;\r
7272     mmi->ptMinTrackSize.x = 100;\r
7273     mmi->ptMinTrackSize.y = 100;\r
7274     break;\r
7275 \r
7276   /* [AS] Snapping */\r
7277   case WM_ENTERSIZEMOVE:\r
7278     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7279 \r
7280   case WM_SIZING:\r
7281     return OnSizing( &sd, hDlg, wParam, lParam );\r
7282 \r
7283   case WM_MOVING:\r
7284     return OnMoving( &sd, hDlg, wParam, lParam );\r
7285 \r
7286   case WM_EXITSIZEMOVE:\r
7287         UpdateICSWidth(hText);\r
7288     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7289   }\r
7290 \r
7291   return DefWindowProc(hDlg, message, wParam, lParam);\r
7292 }\r
7293 \r
7294 \r
7295 VOID\r
7296 ConsoleCreate()\r
7297 {\r
7298   HWND hCons;\r
7299   if (hwndConsole) return;\r
7300   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7301   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7302 }\r
7303 \r
7304 \r
7305 VOID\r
7306 ConsoleOutput(char* data, int length, int forceVisible)\r
7307 {\r
7308   HWND hText;\r
7309   int trim, exlen;\r
7310   char *p, *q;\r
7311   char buf[CO_MAX+1];\r
7312   POINT pEnd;\r
7313   RECT rect;\r
7314   static int delayLF = 0;\r
7315   CHARRANGE savesel, sel;\r
7316 \r
7317   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7318   p = data;\r
7319   q = buf;\r
7320   if (delayLF) {\r
7321     *q++ = '\r';\r
7322     *q++ = '\n';\r
7323     delayLF = 0;\r
7324   }\r
7325   while (length--) {\r
7326     if (*p == '\n') {\r
7327       if (*++p) {\r
7328         *q++ = '\r';\r
7329         *q++ = '\n';\r
7330       } else {\r
7331         delayLF = 1;\r
7332       }\r
7333     } else if (*p == '\007') {\r
7334        MyPlaySound(&sounds[(int)SoundBell]);\r
7335        p++;\r
7336     } else {\r
7337       *q++ = *p++;\r
7338     }\r
7339   }\r
7340   *q = NULLCHAR;\r
7341   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7342   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7343   /* Save current selection */\r
7344   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7345   exlen = GetWindowTextLength(hText);\r
7346   /* Find out whether current end of text is visible */\r
7347   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7348   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7349   /* Trim existing text if it's too long */\r
7350   if (exlen + (q - buf) > CO_MAX) {\r
7351     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7352     sel.cpMin = 0;\r
7353     sel.cpMax = trim;\r
7354     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7355     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7356     exlen -= trim;\r
7357     savesel.cpMin -= trim;\r
7358     savesel.cpMax -= trim;\r
7359     if (exlen < 0) exlen = 0;\r
7360     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7361     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7362   }\r
7363   /* Append the new text */\r
7364   sel.cpMin = exlen;\r
7365   sel.cpMax = exlen;\r
7366   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7367   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7368   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7369   if (forceVisible || exlen == 0 ||\r
7370       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7371        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7372     /* Scroll to make new end of text visible if old end of text\r
7373        was visible or new text is an echo of user typein */\r
7374     sel.cpMin = 9999999;\r
7375     sel.cpMax = 9999999;\r
7376     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7377     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7378     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7379     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7380   }\r
7381   if (savesel.cpMax == exlen || forceVisible) {\r
7382     /* Move insert point to new end of text if it was at the old\r
7383        end of text or if the new text is an echo of user typein */\r
7384     sel.cpMin = 9999999;\r
7385     sel.cpMax = 9999999;\r
7386     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7387   } else {\r
7388     /* Restore previous selection */\r
7389     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7390   }\r
7391   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7392 }\r
7393 \r
7394 /*---------*/\r
7395 \r
7396 \r
7397 void\r
7398 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7399 {\r
7400   char buf[100];\r
7401   char *str;\r
7402   COLORREF oldFg, oldBg;\r
7403   HFONT oldFont;\r
7404   RECT rect;\r
7405 \r
7406   if(copyNumber > 1)
7407     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7408 \r
7409   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7410   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7411   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7412 \r
7413   rect.left = x;\r
7414   rect.right = x + squareSize;\r
7415   rect.top  = y;\r
7416   rect.bottom = y + squareSize;\r
7417   str = buf;\r
7418 \r
7419   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7420                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7421              y, ETO_CLIPPED|ETO_OPAQUE,\r
7422              &rect, str, strlen(str), NULL);\r
7423 \r
7424   (void) SetTextColor(hdc, oldFg);\r
7425   (void) SetBkColor(hdc, oldBg);\r
7426   (void) SelectObject(hdc, oldFont);\r
7427 }\r
7428 \r
7429 void\r
7430 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7431               RECT *rect, char *color, char *flagFell)\r
7432 {\r
7433   char buf[100];\r
7434   char *str;\r
7435   COLORREF oldFg, oldBg;\r
7436   HFONT oldFont;\r
7437 \r
7438   if (appData.clockMode) {\r
7439     if (tinyLayout)\r
7440       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7441     else\r
7442       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7443     str = buf;\r
7444   } else {\r
7445     str = color;\r
7446   }\r
7447 \r
7448   if (highlight) {\r
7449     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7450     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7451   } else {\r
7452     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7453     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7454   }\r
7455   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7456 \r
7457   JAWS_SILENCE\r
7458 \r
7459   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7460              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7461              rect, str, strlen(str), NULL);\r
7462   if(logoHeight > 0 && appData.clockMode) {\r
7463       RECT r;\r
7464       str += strlen(color)+2;\r
7465       r.top = rect->top + logoHeight/2;\r
7466       r.left = rect->left;\r
7467       r.right = rect->right;\r
7468       r.bottom = rect->bottom;\r
7469       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7470                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7471                  &r, str, strlen(str), NULL);\r
7472   }\r
7473   (void) SetTextColor(hdc, oldFg);\r
7474   (void) SetBkColor(hdc, oldBg);\r
7475   (void) SelectObject(hdc, oldFont);\r
7476 }\r
7477 \r
7478 \r
7479 int\r
7480 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7481            OVERLAPPED *ovl)\r
7482 {\r
7483   int ok, err;\r
7484 \r
7485   /* [AS]  */\r
7486   if( count <= 0 ) {\r
7487     if (appData.debugMode) {\r
7488       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7489     }\r
7490 \r
7491     return ERROR_INVALID_USER_BUFFER;\r
7492   }\r
7493 \r
7494   ResetEvent(ovl->hEvent);\r
7495   ovl->Offset = ovl->OffsetHigh = 0;\r
7496   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7497   if (ok) {\r
7498     err = NO_ERROR;\r
7499   } else {\r
7500     err = GetLastError();\r
7501     if (err == ERROR_IO_PENDING) {\r
7502       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7503       if (ok)\r
7504         err = NO_ERROR;\r
7505       else\r
7506         err = GetLastError();\r
7507     }\r
7508   }\r
7509   return err;\r
7510 }\r
7511 \r
7512 int\r
7513 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7514             OVERLAPPED *ovl)\r
7515 {\r
7516   int ok, err;\r
7517 \r
7518   ResetEvent(ovl->hEvent);\r
7519   ovl->Offset = ovl->OffsetHigh = 0;\r
7520   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7521   if (ok) {\r
7522     err = NO_ERROR;\r
7523   } else {\r
7524     err = GetLastError();\r
7525     if (err == ERROR_IO_PENDING) {\r
7526       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7527       if (ok)\r
7528         err = NO_ERROR;\r
7529       else\r
7530         err = GetLastError();\r
7531     }\r
7532   }\r
7533   return err;\r
7534 }\r
7535 \r
7536 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7537 void CheckForInputBufferFull( InputSource * is )\r
7538 {\r
7539     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7540         /* Look for end of line */\r
7541         char * p = is->buf;\r
7542         \r
7543         while( p < is->next && *p != '\n' ) {\r
7544             p++;\r
7545         }\r
7546 \r
7547         if( p >= is->next ) {\r
7548             if (appData.debugMode) {\r
7549                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7550             }\r
7551 \r
7552             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7553             is->count = (DWORD) -1;\r
7554             is->next = is->buf;\r
7555         }\r
7556     }\r
7557 }\r
7558 \r
7559 DWORD\r
7560 InputThread(LPVOID arg)\r
7561 {\r
7562   InputSource *is;\r
7563   OVERLAPPED ovl;\r
7564 \r
7565   is = (InputSource *) arg;\r
7566   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7567   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7568   while (is->hThread != NULL) {\r
7569     is->error = DoReadFile(is->hFile, is->next,\r
7570                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7571                            &is->count, &ovl);\r
7572     if (is->error == NO_ERROR) {\r
7573       is->next += is->count;\r
7574     } else {\r
7575       if (is->error == ERROR_BROKEN_PIPE) {\r
7576         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7577         is->count = 0;\r
7578       } else {\r
7579         is->count = (DWORD) -1;\r
7580         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7581         break; \r
7582       }\r
7583     }\r
7584 \r
7585     CheckForInputBufferFull( is );\r
7586 \r
7587     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7588 \r
7589     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7590 \r
7591     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7592   }\r
7593 \r
7594   CloseHandle(ovl.hEvent);\r
7595   CloseHandle(is->hFile);\r
7596 \r
7597   if (appData.debugMode) {\r
7598     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7599   }\r
7600 \r
7601   return 0;\r
7602 }\r
7603 \r
7604 \r
7605 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7606 DWORD\r
7607 NonOvlInputThread(LPVOID arg)\r
7608 {\r
7609   InputSource *is;\r
7610   char *p, *q;\r
7611   int i;\r
7612   char prev;\r
7613 \r
7614   is = (InputSource *) arg;\r
7615   while (is->hThread != NULL) {\r
7616     is->error = ReadFile(is->hFile, is->next,\r
7617                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7618                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7619     if (is->error == NO_ERROR) {\r
7620       /* Change CRLF to LF */\r
7621       if (is->next > is->buf) {\r
7622         p = is->next - 1;\r
7623         i = is->count + 1;\r
7624       } else {\r
7625         p = is->next;\r
7626         i = is->count;\r
7627       }\r
7628       q = p;\r
7629       prev = NULLCHAR;\r
7630       while (i > 0) {\r
7631         if (prev == '\r' && *p == '\n') {\r
7632           *(q-1) = '\n';\r
7633           is->count--;\r
7634         } else { \r
7635           *q++ = *p;\r
7636         }\r
7637         prev = *p++;\r
7638         i--;\r
7639       }\r
7640       *q = NULLCHAR;\r
7641       is->next = q;\r
7642     } else {\r
7643       if (is->error == ERROR_BROKEN_PIPE) {\r
7644         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7645         is->count = 0; \r
7646       } else {\r
7647         is->count = (DWORD) -1;\r
7648       }\r
7649     }\r
7650 \r
7651     CheckForInputBufferFull( is );\r
7652 \r
7653     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7654 \r
7655     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7656 \r
7657     if (is->count < 0) break;  /* Quit on error */\r
7658   }\r
7659   CloseHandle(is->hFile);\r
7660   return 0;\r
7661 }\r
7662 \r
7663 DWORD\r
7664 SocketInputThread(LPVOID arg)\r
7665 {\r
7666   InputSource *is;\r
7667 \r
7668   is = (InputSource *) arg;\r
7669   while (is->hThread != NULL) {\r
7670     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7671     if ((int)is->count == SOCKET_ERROR) {\r
7672       is->count = (DWORD) -1;\r
7673       is->error = WSAGetLastError();\r
7674     } else {\r
7675       is->error = NO_ERROR;\r
7676       is->next += is->count;\r
7677       if (is->count == 0 && is->second == is) {\r
7678         /* End of file on stderr; quit with no message */\r
7679         break;\r
7680       }\r
7681     }\r
7682     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7683 \r
7684     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7685 \r
7686     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7687   }\r
7688   return 0;\r
7689 }\r
7690 \r
7691 VOID\r
7692 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7693 {\r
7694   InputSource *is;\r
7695 \r
7696   is = (InputSource *) lParam;\r
7697   if (is->lineByLine) {\r
7698     /* Feed in lines one by one */\r
7699     char *p = is->buf;\r
7700     char *q = p;\r
7701     while (q < is->next) {\r
7702       if (*q++ == '\n') {\r
7703         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7704         p = q;\r
7705       }\r
7706     }\r
7707     \r
7708     /* Move any partial line to the start of the buffer */\r
7709     q = is->buf;\r
7710     while (p < is->next) {\r
7711       *q++ = *p++;\r
7712     }\r
7713     is->next = q;\r
7714 \r
7715     if (is->error != NO_ERROR || is->count == 0) {\r
7716       /* Notify backend of the error.  Note: If there was a partial\r
7717          line at the end, it is not flushed through. */\r
7718       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7719     }\r
7720   } else {\r
7721     /* Feed in the whole chunk of input at once */\r
7722     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7723     is->next = is->buf;\r
7724   }\r
7725 }\r
7726 \r
7727 /*---------------------------------------------------------------------------*\\r
7728  *\r
7729  *  Menu enables. Used when setting various modes.\r
7730  *\r
7731 \*---------------------------------------------------------------------------*/\r
7732 \r
7733 typedef struct {\r
7734   int item;\r
7735   int flags;\r
7736 } Enables;\r
7737 \r
7738 VOID\r
7739 GreyRevert(Boolean grey)\r
7740 { // [HGM] vari: for retracting variations in local mode\r
7741   HMENU hmenu = GetMenu(hwndMain);\r
7742   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7743   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7744 }\r
7745 \r
7746 VOID\r
7747 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7748 {\r
7749   while (enab->item > 0) {\r
7750     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7751     enab++;\r
7752   }\r
7753 }\r
7754 \r
7755 Enables gnuEnables[] = {\r
7756   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7757   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7758   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7759   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7760   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7761   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7762   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7763   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7764   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7765   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7766   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7767   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7768   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7769   { -1, -1 }\r
7770 };\r
7771 \r
7772 Enables icsEnables[] = {\r
7773   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7774   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7775   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7776   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7777   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7778   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7779   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7780   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7781   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7782   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7783   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7784   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7785   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7786   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7787   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7788   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7789   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7790   { -1, -1 }\r
7791 };\r
7792 \r
7793 #if ZIPPY\r
7794 Enables zippyEnables[] = {\r
7795   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7796   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7797   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7798   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7799   { -1, -1 }\r
7800 };\r
7801 #endif\r
7802 \r
7803 Enables ncpEnables[] = {\r
7804   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7805   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7806   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7807   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7808   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7809   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7810   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7811   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7812   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7813   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7814   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7815   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7816   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7817   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7818   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7819   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7820   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7821   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7822   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7823   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7824   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7825   { -1, -1 }\r
7826 };\r
7827 \r
7828 Enables trainingOnEnables[] = {\r
7829   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7830   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7831   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7832   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7833   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7834   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7835   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7836   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7837   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7838   { -1, -1 }\r
7839 };\r
7840 \r
7841 Enables trainingOffEnables[] = {\r
7842   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7843   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7844   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7845   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7846   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7847   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7848   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7849   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7850   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7851   { -1, -1 }\r
7852 };\r
7853 \r
7854 /* These modify either ncpEnables or gnuEnables */\r
7855 Enables cmailEnables[] = {\r
7856   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7857   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7858   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7859   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7860   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7861   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7862   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7863   { -1, -1 }\r
7864 };\r
7865 \r
7866 Enables machineThinkingEnables[] = {\r
7867   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7868   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7869   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7870   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7871   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7872   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7873   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7874   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7875   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7876   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7877   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7878   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7879   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7880   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7881   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7882   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7883   { -1, -1 }\r
7884 };\r
7885 \r
7886 Enables userThinkingEnables[] = {\r
7887   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7888   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7889   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7890   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7891   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7892   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7893   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7894   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7895   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7896   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7897   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7898   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7899   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7900   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7901   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7902   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7903   { -1, -1 }\r
7904 };\r
7905 \r
7906 /*---------------------------------------------------------------------------*\\r
7907  *\r
7908  *  Front-end interface functions exported by XBoard.\r
7909  *  Functions appear in same order as prototypes in frontend.h.\r
7910  * \r
7911 \*---------------------------------------------------------------------------*/\r
7912 VOID\r
7913 ModeHighlight()\r
7914 {\r
7915   static UINT prevChecked = 0;\r
7916   static int prevPausing = 0;\r
7917   UINT nowChecked;\r
7918 \r
7919   if (pausing != prevPausing) {\r
7920     prevPausing = pausing;\r
7921     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7922                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7923     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7924   }\r
7925 \r
7926   switch (gameMode) {\r
7927   case BeginningOfGame:\r
7928     if (appData.icsActive)\r
7929       nowChecked = IDM_IcsClient;\r
7930     else if (appData.noChessProgram)\r
7931       nowChecked = IDM_EditGame;\r
7932     else\r
7933       nowChecked = IDM_MachineBlack;\r
7934     break;\r
7935   case MachinePlaysBlack:\r
7936     nowChecked = IDM_MachineBlack;\r
7937     break;\r
7938   case MachinePlaysWhite:\r
7939     nowChecked = IDM_MachineWhite;\r
7940     break;\r
7941   case TwoMachinesPlay:\r
7942     nowChecked = matchMode ? IDM_Match : IDM_TwoMachines; // [HGM] match\r
7943     break;\r
7944   case AnalyzeMode:\r
7945     nowChecked = IDM_AnalysisMode;\r
7946     break;\r
7947   case AnalyzeFile:\r
7948     nowChecked = IDM_AnalyzeFile;\r
7949     break;\r
7950   case EditGame:\r
7951     nowChecked = IDM_EditGame;\r
7952     break;\r
7953   case PlayFromGameFile:\r
7954     nowChecked = IDM_LoadGame;\r
7955     break;\r
7956   case EditPosition:\r
7957     nowChecked = IDM_EditPosition;\r
7958     break;\r
7959   case Training:\r
7960     nowChecked = IDM_Training;\r
7961     break;\r
7962   case IcsPlayingWhite:\r
7963   case IcsPlayingBlack:\r
7964   case IcsObserving:\r
7965   case IcsIdle:\r
7966     nowChecked = IDM_IcsClient;\r
7967     break;\r
7968   default:\r
7969   case EndOfGame:\r
7970     nowChecked = 0;\r
7971     break;\r
7972   }\r
7973   if (prevChecked != 0)\r
7974     (void) CheckMenuItem(GetMenu(hwndMain),\r
7975                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
7976   if (nowChecked != 0)\r
7977     (void) CheckMenuItem(GetMenu(hwndMain),\r
7978                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
7979 \r
7980   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
7981     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
7982                           MF_BYCOMMAND|MF_ENABLED);\r
7983   } else {\r
7984     (void) EnableMenuItem(GetMenu(hwndMain), \r
7985                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
7986   }\r
7987 \r
7988   prevChecked = nowChecked;\r
7989 \r
7990   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
7991   if (appData.icsActive) {\r
7992        if (appData.icsEngineAnalyze) {\r
7993                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7994                        MF_BYCOMMAND|MF_CHECKED);\r
7995        } else {\r
7996                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7997                        MF_BYCOMMAND|MF_UNCHECKED);\r
7998        }\r
7999   }\r
8000 }\r
8001 \r
8002 VOID\r
8003 SetICSMode()\r
8004 {\r
8005   HMENU hmenu = GetMenu(hwndMain);\r
8006   SetMenuEnables(hmenu, icsEnables);\r
8007   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8008     MF_BYPOSITION|MF_ENABLED);\r
8009 #if ZIPPY\r
8010   if (appData.zippyPlay) {\r
8011     SetMenuEnables(hmenu, zippyEnables);\r
8012     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8013          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8014           MF_BYCOMMAND|MF_ENABLED);\r
8015   }\r
8016 #endif\r
8017 }\r
8018 \r
8019 VOID\r
8020 SetGNUMode()\r
8021 {\r
8022   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8023 }\r
8024 \r
8025 VOID\r
8026 SetNCPMode()\r
8027 {\r
8028   HMENU hmenu = GetMenu(hwndMain);\r
8029   SetMenuEnables(hmenu, ncpEnables);\r
8030   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8031     MF_BYPOSITION|MF_GRAYED);\r
8032     DrawMenuBar(hwndMain);\r
8033 }\r
8034 \r
8035 VOID\r
8036 SetCmailMode()\r
8037 {\r
8038   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8039 }\r
8040 \r
8041 VOID \r
8042 SetTrainingModeOn()\r
8043 {\r
8044   int i;\r
8045   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8046   for (i = 0; i < N_BUTTONS; i++) {\r
8047     if (buttonDesc[i].hwnd != NULL)\r
8048       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8049   }\r
8050   CommentPopDown();\r
8051 }\r
8052 \r
8053 VOID SetTrainingModeOff()\r
8054 {\r
8055   int i;\r
8056   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8057   for (i = 0; i < N_BUTTONS; i++) {\r
8058     if (buttonDesc[i].hwnd != NULL)\r
8059       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8060   }\r
8061 }\r
8062 \r
8063 \r
8064 VOID\r
8065 SetUserThinkingEnables()\r
8066 {\r
8067   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8068 }\r
8069 \r
8070 VOID\r
8071 SetMachineThinkingEnables()\r
8072 {\r
8073   HMENU hMenu = GetMenu(hwndMain);\r
8074   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8075 \r
8076   SetMenuEnables(hMenu, machineThinkingEnables);\r
8077 \r
8078   if (gameMode == MachinePlaysBlack) {\r
8079     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8080   } else if (gameMode == MachinePlaysWhite) {\r
8081     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8082   } else if (gameMode == TwoMachinesPlay) {\r
8083     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8084   }\r
8085 }\r
8086 \r
8087 \r
8088 VOID\r
8089 DisplayTitle(char *str)\r
8090 {\r
8091   char title[MSG_SIZ], *host;\r
8092   if (str[0] != NULLCHAR) {\r
8093     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8094   } else if (appData.icsActive) {\r
8095     if (appData.icsCommPort[0] != NULLCHAR)\r
8096       host = "ICS";\r
8097     else \r
8098       host = appData.icsHost;\r
8099       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8100   } else if (appData.noChessProgram) {\r
8101     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8102   } else {\r
8103     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8104     strcat(title, ": ");\r
8105     strcat(title, first.tidy);\r
8106   }\r
8107   SetWindowText(hwndMain, title);\r
8108 }\r
8109 \r
8110 \r
8111 VOID\r
8112 DisplayMessage(char *str1, char *str2)\r
8113 {\r
8114   HDC hdc;\r
8115   HFONT oldFont;\r
8116   int remain = MESSAGE_TEXT_MAX - 1;\r
8117   int len;\r
8118 \r
8119   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8120   messageText[0] = NULLCHAR;\r
8121   if (*str1) {\r
8122     len = strlen(str1);\r
8123     if (len > remain) len = remain;\r
8124     strncpy(messageText, str1, len);\r
8125     messageText[len] = NULLCHAR;\r
8126     remain -= len;\r
8127   }\r
8128   if (*str2 && remain >= 2) {\r
8129     if (*str1) {\r
8130       strcat(messageText, "  ");\r
8131       remain -= 2;\r
8132     }\r
8133     len = strlen(str2);\r
8134     if (len > remain) len = remain;\r
8135     strncat(messageText, str2, len);\r
8136   }\r
8137   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8138 \r
8139   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8140 \r
8141   SAYMACHINEMOVE();\r
8142 \r
8143   hdc = GetDC(hwndMain);\r
8144   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8145   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8146              &messageRect, messageText, strlen(messageText), NULL);\r
8147   (void) SelectObject(hdc, oldFont);\r
8148   (void) ReleaseDC(hwndMain, hdc);\r
8149 }\r
8150 \r
8151 VOID\r
8152 DisplayError(char *str, int error)\r
8153 {\r
8154   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8155   int len;\r
8156 \r
8157   if (error == 0) {\r
8158     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8159   } else {\r
8160     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8161                         NULL, error, LANG_NEUTRAL,\r
8162                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8163     if (len > 0) {\r
8164       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8165     } else {\r
8166       ErrorMap *em = errmap;\r
8167       while (em->err != 0 && em->err != error) em++;\r
8168       if (em->err != 0) {\r
8169         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8170       } else {\r
8171         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8172       }\r
8173     }\r
8174   }\r
8175   \r
8176   ErrorPopUp(_("Error"), buf);\r
8177 }\r
8178 \r
8179 \r
8180 VOID\r
8181 DisplayMoveError(char *str)\r
8182 {\r
8183   fromX = fromY = -1;\r
8184   ClearHighlights();\r
8185   DrawPosition(FALSE, NULL);\r
8186   if (appData.popupMoveErrors) {\r
8187     ErrorPopUp(_("Error"), str);\r
8188   } else {\r
8189     DisplayMessage(str, "");\r
8190     moveErrorMessageUp = TRUE;\r
8191   }\r
8192 }\r
8193 \r
8194 VOID\r
8195 DisplayFatalError(char *str, int error, int exitStatus)\r
8196 {\r
8197   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8198   int len;\r
8199   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8200 \r
8201   if (error != 0) {\r
8202     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8203                         NULL, error, LANG_NEUTRAL,\r
8204                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8205     if (len > 0) {\r
8206       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8207     } else {\r
8208       ErrorMap *em = errmap;\r
8209       while (em->err != 0 && em->err != error) em++;\r
8210       if (em->err != 0) {\r
8211         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8212       } else {\r
8213         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8214       }\r
8215     }\r
8216     str = buf;\r
8217   }\r
8218   if (appData.debugMode) {\r
8219     fprintf(debugFP, "%s: %s\n", label, str);\r
8220   }\r
8221   if (appData.popupExitMessage) {\r
8222     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8223                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8224   }\r
8225   ExitEvent(exitStatus);\r
8226 }\r
8227 \r
8228 \r
8229 VOID\r
8230 DisplayInformation(char *str)\r
8231 {\r
8232   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8233 }\r
8234 \r
8235 \r
8236 VOID\r
8237 DisplayNote(char *str)\r
8238 {\r
8239   ErrorPopUp(_("Note"), str);\r
8240 }\r
8241 \r
8242 \r
8243 typedef struct {\r
8244   char *title, *question, *replyPrefix;\r
8245   ProcRef pr;\r
8246 } QuestionParams;\r
8247 \r
8248 LRESULT CALLBACK\r
8249 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8250 {\r
8251   static QuestionParams *qp;\r
8252   char reply[MSG_SIZ];\r
8253   int len, err;\r
8254 \r
8255   switch (message) {\r
8256   case WM_INITDIALOG:\r
8257     qp = (QuestionParams *) lParam;\r
8258     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8259     Translate(hDlg, DLG_Question);\r
8260     SetWindowText(hDlg, qp->title);\r
8261     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8262     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8263     return FALSE;\r
8264 \r
8265   case WM_COMMAND:\r
8266     switch (LOWORD(wParam)) {\r
8267     case IDOK:\r
8268       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8269       if (*reply) strcat(reply, " ");\r
8270       len = strlen(reply);\r
8271       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8272       strcat(reply, "\n");\r
8273       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8274       EndDialog(hDlg, TRUE);\r
8275       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8276       return TRUE;\r
8277     case IDCANCEL:\r
8278       EndDialog(hDlg, FALSE);\r
8279       return TRUE;\r
8280     default:\r
8281       break;\r
8282     }\r
8283     break;\r
8284   }\r
8285   return FALSE;\r
8286 }\r
8287 \r
8288 VOID\r
8289 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8290 {\r
8291     QuestionParams qp;\r
8292     FARPROC lpProc;\r
8293     \r
8294     qp.title = title;\r
8295     qp.question = question;\r
8296     qp.replyPrefix = replyPrefix;\r
8297     qp.pr = pr;\r
8298     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8299     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8300       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8301     FreeProcInstance(lpProc);\r
8302 }\r
8303 \r
8304 /* [AS] Pick FRC position */\r
8305 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8306 {\r
8307     static int * lpIndexFRC;\r
8308     BOOL index_is_ok;\r
8309     char buf[16];\r
8310 \r
8311     switch( message )\r
8312     {\r
8313     case WM_INITDIALOG:\r
8314         lpIndexFRC = (int *) lParam;\r
8315 \r
8316         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8317         Translate(hDlg, DLG_NewGameFRC);\r
8318 \r
8319         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8320         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8321         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8322         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8323 \r
8324         break;\r
8325 \r
8326     case WM_COMMAND:\r
8327         switch( LOWORD(wParam) ) {\r
8328         case IDOK:\r
8329             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8330             EndDialog( hDlg, 0 );\r
8331             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8332             return TRUE;\r
8333         case IDCANCEL:\r
8334             EndDialog( hDlg, 1 );   \r
8335             return TRUE;\r
8336         case IDC_NFG_Edit:\r
8337             if( HIWORD(wParam) == EN_CHANGE ) {\r
8338                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8339 \r
8340                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8341             }\r
8342             return TRUE;\r
8343         case IDC_NFG_Random:\r
8344           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8345             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8346             return TRUE;\r
8347         }\r
8348 \r
8349         break;\r
8350     }\r
8351 \r
8352     return FALSE;\r
8353 }\r
8354 \r
8355 int NewGameFRC()\r
8356 {\r
8357     int result;\r
8358     int index = appData.defaultFrcPosition;\r
8359     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8360 \r
8361     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8362 \r
8363     if( result == 0 ) {\r
8364         appData.defaultFrcPosition = index;\r
8365     }\r
8366 \r
8367     return result;\r
8368 }\r
8369 \r
8370 /* [AS] Game list options. Refactored by HGM */\r
8371 \r
8372 HWND gameListOptionsDialog;\r
8373 \r
8374 // low-level front-end: clear text edit / list widget\r
8375 void\r
8376 GLT_ClearList()\r
8377 {\r
8378     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8379 }\r
8380 \r
8381 // low-level front-end: clear text edit / list widget\r
8382 void\r
8383 GLT_DeSelectList()\r
8384 {\r
8385     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8386 }\r
8387 \r
8388 // low-level front-end: append line to text edit / list widget\r
8389 void\r
8390 GLT_AddToList( char *name )\r
8391 {\r
8392     if( name != 0 ) {\r
8393             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8394     }\r
8395 }\r
8396 \r
8397 // low-level front-end: get line from text edit / list widget\r
8398 Boolean\r
8399 GLT_GetFromList( int index, char *name )\r
8400 {\r
8401     if( name != 0 ) {\r
8402             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8403                 return TRUE;\r
8404     }\r
8405     return FALSE;\r
8406 }\r
8407 \r
8408 void GLT_MoveSelection( HWND hDlg, int delta )\r
8409 {\r
8410     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8411     int idx2 = idx1 + delta;\r
8412     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8413 \r
8414     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8415         char buf[128];\r
8416 \r
8417         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8418         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8419         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8420         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8421     }\r
8422 }\r
8423 \r
8424 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8425 {\r
8426     switch( message )\r
8427     {\r
8428     case WM_INITDIALOG:\r
8429         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8430         \r
8431         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8432         Translate(hDlg, DLG_GameListOptions);\r
8433 \r
8434         /* Initialize list */\r
8435         GLT_TagsToList( lpUserGLT );\r
8436 \r
8437         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8438 \r
8439         break;\r
8440 \r
8441     case WM_COMMAND:\r
8442         switch( LOWORD(wParam) ) {\r
8443         case IDOK:\r
8444             GLT_ParseList();\r
8445             EndDialog( hDlg, 0 );\r
8446             return TRUE;\r
8447         case IDCANCEL:\r
8448             EndDialog( hDlg, 1 );\r
8449             return TRUE;\r
8450 \r
8451         case IDC_GLT_Default:\r
8452             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8453             return TRUE;\r
8454 \r
8455         case IDC_GLT_Restore:\r
8456             GLT_TagsToList( appData.gameListTags );\r
8457             return TRUE;\r
8458 \r
8459         case IDC_GLT_Up:\r
8460             GLT_MoveSelection( hDlg, -1 );\r
8461             return TRUE;\r
8462 \r
8463         case IDC_GLT_Down:\r
8464             GLT_MoveSelection( hDlg, +1 );\r
8465             return TRUE;\r
8466         }\r
8467 \r
8468         break;\r
8469     }\r
8470 \r
8471     return FALSE;\r
8472 }\r
8473 \r
8474 int GameListOptions()\r
8475 {\r
8476     int result;\r
8477     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8478 \r
8479       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8480 \r
8481     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8482 \r
8483     if( result == 0 ) {\r
8484         /* [AS] Memory leak here! */\r
8485         appData.gameListTags = strdup( lpUserGLT ); \r
8486     }\r
8487 \r
8488     return result;\r
8489 }\r
8490 \r
8491 VOID\r
8492 DisplayIcsInteractionTitle(char *str)\r
8493 {\r
8494   char consoleTitle[MSG_SIZ];\r
8495 \r
8496     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8497   SetWindowText(hwndConsole, consoleTitle);\r
8498 }\r
8499 \r
8500 void\r
8501 DrawPosition(int fullRedraw, Board board)\r
8502 {\r
8503   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8504 }\r
8505 \r
8506 void NotifyFrontendLogin()\r
8507 {\r
8508         if (hwndConsole)\r
8509                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8510 }\r
8511 \r
8512 VOID\r
8513 ResetFrontEnd()\r
8514 {\r
8515   fromX = fromY = -1;\r
8516   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8517     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8518     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8519     dragInfo.lastpos = dragInfo.pos;\r
8520     dragInfo.start.x = dragInfo.start.y = -1;\r
8521     dragInfo.from = dragInfo.start;\r
8522     ReleaseCapture();\r
8523     DrawPosition(TRUE, NULL);\r
8524   }\r
8525   TagsPopDown();\r
8526 }\r
8527 \r
8528 \r
8529 VOID\r
8530 CommentPopUp(char *title, char *str)\r
8531 {\r
8532   HWND hwnd = GetActiveWindow();\r
8533   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8534   SAY(str);\r
8535   SetActiveWindow(hwnd);\r
8536 }\r
8537 \r
8538 VOID\r
8539 CommentPopDown(void)\r
8540 {\r
8541   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8542   if (commentDialog) {\r
8543     ShowWindow(commentDialog, SW_HIDE);\r
8544   }\r
8545   commentUp = FALSE;\r
8546 }\r
8547 \r
8548 VOID\r
8549 EditCommentPopUp(int index, char *title, char *str)\r
8550 {\r
8551   EitherCommentPopUp(index, title, str, TRUE);\r
8552 }\r
8553 \r
8554 \r
8555 VOID\r
8556 RingBell()\r
8557 {\r
8558   MyPlaySound(&sounds[(int)SoundMove]);\r
8559 }\r
8560 \r
8561 VOID PlayIcsWinSound()\r
8562 {\r
8563   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8564 }\r
8565 \r
8566 VOID PlayIcsLossSound()\r
8567 {\r
8568   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8569 }\r
8570 \r
8571 VOID PlayIcsDrawSound()\r
8572 {\r
8573   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8574 }\r
8575 \r
8576 VOID PlayIcsUnfinishedSound()\r
8577 {\r
8578   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8579 }\r
8580 \r
8581 VOID\r
8582 PlayAlarmSound()\r
8583 {\r
8584   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8585 }\r
8586 \r
8587 \r
8588 VOID\r
8589 EchoOn()\r
8590 {\r
8591   HWND hInput;\r
8592   consoleEcho = TRUE;\r
8593   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8594   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8595   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8596 }\r
8597 \r
8598 \r
8599 VOID\r
8600 EchoOff()\r
8601 {\r
8602   CHARFORMAT cf;\r
8603   HWND hInput;\r
8604   consoleEcho = FALSE;\r
8605   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8606   /* This works OK: set text and background both to the same color */\r
8607   cf = consoleCF;\r
8608   cf.crTextColor = COLOR_ECHOOFF;\r
8609   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8610   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8611 }\r
8612 \r
8613 /* No Raw()...? */\r
8614 \r
8615 void Colorize(ColorClass cc, int continuation)\r
8616 {\r
8617   currentColorClass = cc;\r
8618   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8619   consoleCF.crTextColor = textAttribs[cc].color;\r
8620   consoleCF.dwEffects = textAttribs[cc].effects;\r
8621   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8622 }\r
8623 \r
8624 char *\r
8625 UserName()\r
8626 {\r
8627   static char buf[MSG_SIZ];\r
8628   DWORD bufsiz = MSG_SIZ;\r
8629 \r
8630   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8631         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8632   }\r
8633   if (!GetUserName(buf, &bufsiz)) {\r
8634     /*DisplayError("Error getting user name", GetLastError());*/\r
8635     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8636   }\r
8637   return buf;\r
8638 }\r
8639 \r
8640 char *\r
8641 HostName()\r
8642 {\r
8643   static char buf[MSG_SIZ];\r
8644   DWORD bufsiz = MSG_SIZ;\r
8645 \r
8646   if (!GetComputerName(buf, &bufsiz)) {\r
8647     /*DisplayError("Error getting host name", GetLastError());*/\r
8648     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8649   }\r
8650   return buf;\r
8651 }\r
8652 \r
8653 \r
8654 int\r
8655 ClockTimerRunning()\r
8656 {\r
8657   return clockTimerEvent != 0;\r
8658 }\r
8659 \r
8660 int\r
8661 StopClockTimer()\r
8662 {\r
8663   if (clockTimerEvent == 0) return FALSE;\r
8664   KillTimer(hwndMain, clockTimerEvent);\r
8665   clockTimerEvent = 0;\r
8666   return TRUE;\r
8667 }\r
8668 \r
8669 void\r
8670 StartClockTimer(long millisec)\r
8671 {\r
8672   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8673                              (UINT) millisec, NULL);\r
8674 }\r
8675 \r
8676 void\r
8677 DisplayWhiteClock(long timeRemaining, int highlight)\r
8678 {\r
8679   HDC hdc;\r
8680   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8681 \r
8682   if(appData.noGUI) return;\r
8683   hdc = GetDC(hwndMain);\r
8684   if (!IsIconic(hwndMain)) {\r
8685     DisplayAClock(hdc, timeRemaining, highlight, \r
8686                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8687   }\r
8688   if (highlight && iconCurrent == iconBlack) {\r
8689     iconCurrent = iconWhite;\r
8690     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8691     if (IsIconic(hwndMain)) {\r
8692       DrawIcon(hdc, 2, 2, iconCurrent);\r
8693     }\r
8694   }\r
8695   (void) ReleaseDC(hwndMain, hdc);\r
8696   if (hwndConsole)\r
8697     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8698 }\r
8699 \r
8700 void\r
8701 DisplayBlackClock(long timeRemaining, int highlight)\r
8702 {\r
8703   HDC hdc;\r
8704   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8705 \r
8706   if(appData.noGUI) return;\r
8707   hdc = GetDC(hwndMain);\r
8708   if (!IsIconic(hwndMain)) {\r
8709     DisplayAClock(hdc, timeRemaining, highlight, \r
8710                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8711   }\r
8712   if (highlight && iconCurrent == iconWhite) {\r
8713     iconCurrent = iconBlack;\r
8714     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8715     if (IsIconic(hwndMain)) {\r
8716       DrawIcon(hdc, 2, 2, iconCurrent);\r
8717     }\r
8718   }\r
8719   (void) ReleaseDC(hwndMain, hdc);\r
8720   if (hwndConsole)\r
8721     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8722 }\r
8723 \r
8724 \r
8725 int\r
8726 LoadGameTimerRunning()\r
8727 {\r
8728   return loadGameTimerEvent != 0;\r
8729 }\r
8730 \r
8731 int\r
8732 StopLoadGameTimer()\r
8733 {\r
8734   if (loadGameTimerEvent == 0) return FALSE;\r
8735   KillTimer(hwndMain, loadGameTimerEvent);\r
8736   loadGameTimerEvent = 0;\r
8737   return TRUE;\r
8738 }\r
8739 \r
8740 void\r
8741 StartLoadGameTimer(long millisec)\r
8742 {\r
8743   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8744                                 (UINT) millisec, NULL);\r
8745 }\r
8746 \r
8747 void\r
8748 AutoSaveGame()\r
8749 {\r
8750   char *defName;\r
8751   FILE *f;\r
8752   char fileTitle[MSG_SIZ];\r
8753 \r
8754   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8755   f = OpenFileDialog(hwndMain, "a", defName,\r
8756                      appData.oldSaveStyle ? "gam" : "pgn",\r
8757                      GAME_FILT, \r
8758                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8759   if (f != NULL) {\r
8760     SaveGame(f, 0, "");\r
8761     fclose(f);\r
8762   }\r
8763 }\r
8764 \r
8765 \r
8766 void\r
8767 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8768 {\r
8769   if (delayedTimerEvent != 0) {\r
8770     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8771       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8772     }\r
8773     KillTimer(hwndMain, delayedTimerEvent);\r
8774     delayedTimerEvent = 0;\r
8775     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8776     delayedTimerCallback();\r
8777   }\r
8778   delayedTimerCallback = cb;\r
8779   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8780                                 (UINT) millisec, NULL);\r
8781 }\r
8782 \r
8783 DelayedEventCallback\r
8784 GetDelayedEvent()\r
8785 {\r
8786   if (delayedTimerEvent) {\r
8787     return delayedTimerCallback;\r
8788   } else {\r
8789     return NULL;\r
8790   }\r
8791 }\r
8792 \r
8793 void\r
8794 CancelDelayedEvent()\r
8795 {\r
8796   if (delayedTimerEvent) {\r
8797     KillTimer(hwndMain, delayedTimerEvent);\r
8798     delayedTimerEvent = 0;\r
8799   }\r
8800 }\r
8801 \r
8802 DWORD GetWin32Priority(int nice)\r
8803 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8804 /*\r
8805 REALTIME_PRIORITY_CLASS     0x00000100\r
8806 HIGH_PRIORITY_CLASS         0x00000080\r
8807 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8808 NORMAL_PRIORITY_CLASS       0x00000020\r
8809 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8810 IDLE_PRIORITY_CLASS         0x00000040\r
8811 */\r
8812         if (nice < -15) return 0x00000080;\r
8813         if (nice < 0)   return 0x00008000;\r
8814         if (nice == 0)  return 0x00000020;\r
8815         if (nice < 15)  return 0x00004000;\r
8816         return 0x00000040;\r
8817 }\r
8818 \r
8819 /* Start a child process running the given program.\r
8820    The process's standard output can be read from "from", and its\r
8821    standard input can be written to "to".\r
8822    Exit with fatal error if anything goes wrong.\r
8823    Returns an opaque pointer that can be used to destroy the process\r
8824    later.\r
8825 */\r
8826 int\r
8827 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8828 {\r
8829 #define BUFSIZE 4096\r
8830 \r
8831   HANDLE hChildStdinRd, hChildStdinWr,\r
8832     hChildStdoutRd, hChildStdoutWr;\r
8833   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8834   SECURITY_ATTRIBUTES saAttr;\r
8835   BOOL fSuccess;\r
8836   PROCESS_INFORMATION piProcInfo;\r
8837   STARTUPINFO siStartInfo;\r
8838   ChildProc *cp;\r
8839   char buf[MSG_SIZ];\r
8840   DWORD err;\r
8841 \r
8842   if (appData.debugMode) {\r
8843     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8844   }\r
8845 \r
8846   *pr = NoProc;\r
8847 \r
8848   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8849   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8850   saAttr.bInheritHandle = TRUE;\r
8851   saAttr.lpSecurityDescriptor = NULL;\r
8852 \r
8853   /*\r
8854    * The steps for redirecting child's STDOUT:\r
8855    *     1. Create anonymous pipe to be STDOUT for child.\r
8856    *     2. Create a noninheritable duplicate of read handle,\r
8857    *         and close the inheritable read handle.\r
8858    */\r
8859 \r
8860   /* Create a pipe for the child's STDOUT. */\r
8861   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8862     return GetLastError();\r
8863   }\r
8864 \r
8865   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8866   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8867                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8868                              FALSE,     /* not inherited */\r
8869                              DUPLICATE_SAME_ACCESS);\r
8870   if (! fSuccess) {\r
8871     return GetLastError();\r
8872   }\r
8873   CloseHandle(hChildStdoutRd);\r
8874 \r
8875   /*\r
8876    * The steps for redirecting child's STDIN:\r
8877    *     1. Create anonymous pipe to be STDIN for child.\r
8878    *     2. Create a noninheritable duplicate of write handle,\r
8879    *         and close the inheritable write handle.\r
8880    */\r
8881 \r
8882   /* Create a pipe for the child's STDIN. */\r
8883   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8884     return GetLastError();\r
8885   }\r
8886 \r
8887   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8888   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8889                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8890                              FALSE,     /* not inherited */\r
8891                              DUPLICATE_SAME_ACCESS);\r
8892   if (! fSuccess) {\r
8893     return GetLastError();\r
8894   }\r
8895   CloseHandle(hChildStdinWr);\r
8896 \r
8897   /* Arrange to (1) look in dir for the child .exe file, and\r
8898    * (2) have dir be the child's working directory.  Interpret\r
8899    * dir relative to the directory WinBoard loaded from. */\r
8900   GetCurrentDirectory(MSG_SIZ, buf);\r
8901   SetCurrentDirectory(installDir);\r
8902   SetCurrentDirectory(dir);\r
8903 \r
8904   /* Now create the child process. */\r
8905 \r
8906   siStartInfo.cb = sizeof(STARTUPINFO);\r
8907   siStartInfo.lpReserved = NULL;\r
8908   siStartInfo.lpDesktop = NULL;\r
8909   siStartInfo.lpTitle = NULL;\r
8910   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8911   siStartInfo.cbReserved2 = 0;\r
8912   siStartInfo.lpReserved2 = NULL;\r
8913   siStartInfo.hStdInput = hChildStdinRd;\r
8914   siStartInfo.hStdOutput = hChildStdoutWr;\r
8915   siStartInfo.hStdError = hChildStdoutWr;\r
8916 \r
8917   fSuccess = CreateProcess(NULL,\r
8918                            cmdLine,        /* command line */\r
8919                            NULL,           /* process security attributes */\r
8920                            NULL,           /* primary thread security attrs */\r
8921                            TRUE,           /* handles are inherited */\r
8922                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8923                            NULL,           /* use parent's environment */\r
8924                            NULL,\r
8925                            &siStartInfo, /* STARTUPINFO pointer */\r
8926                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
8927 \r
8928   err = GetLastError();\r
8929   SetCurrentDirectory(buf); /* return to prev directory */\r
8930   if (! fSuccess) {\r
8931     return err;\r
8932   }\r
8933 \r
8934   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
8935     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
8936     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
8937   }\r
8938 \r
8939   /* Close the handles we don't need in the parent */\r
8940   CloseHandle(piProcInfo.hThread);\r
8941   CloseHandle(hChildStdinRd);\r
8942   CloseHandle(hChildStdoutWr);\r
8943 \r
8944   /* Prepare return value */\r
8945   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8946   cp->kind = CPReal;\r
8947   cp->hProcess = piProcInfo.hProcess;\r
8948   cp->pid = piProcInfo.dwProcessId;\r
8949   cp->hFrom = hChildStdoutRdDup;\r
8950   cp->hTo = hChildStdinWrDup;\r
8951 \r
8952   *pr = (void *) cp;\r
8953 \r
8954   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
8955      2000 where engines sometimes don't see the initial command(s)\r
8956      from WinBoard and hang.  I don't understand how that can happen,\r
8957      but the Sleep is harmless, so I've put it in.  Others have also\r
8958      reported what may be the same problem, so hopefully this will fix\r
8959      it for them too.  */\r
8960   Sleep(500);\r
8961 \r
8962   return NO_ERROR;\r
8963 }\r
8964 \r
8965 \r
8966 void\r
8967 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
8968 {\r
8969   ChildProc *cp; int result;\r
8970 \r
8971   cp = (ChildProc *) pr;\r
8972   if (cp == NULL) return;\r
8973 \r
8974   switch (cp->kind) {\r
8975   case CPReal:\r
8976     /* TerminateProcess is considered harmful, so... */\r
8977     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
8978     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
8979     /* The following doesn't work because the chess program\r
8980        doesn't "have the same console" as WinBoard.  Maybe\r
8981        we could arrange for this even though neither WinBoard\r
8982        nor the chess program uses a console for stdio? */\r
8983     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
8984 \r
8985     /* [AS] Special termination modes for misbehaving programs... */\r
8986     if( signal == 9 ) { \r
8987         result = TerminateProcess( cp->hProcess, 0 );\r
8988 \r
8989         if ( appData.debugMode) {\r
8990             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
8991         }\r
8992     }\r
8993     else if( signal == 10 ) {\r
8994         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
8995 \r
8996         if( dw != WAIT_OBJECT_0 ) {\r
8997             result = TerminateProcess( cp->hProcess, 0 );\r
8998 \r
8999             if ( appData.debugMode) {\r
9000                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9001             }\r
9002 \r
9003         }\r
9004     }\r
9005 \r
9006     CloseHandle(cp->hProcess);\r
9007     break;\r
9008 \r
9009   case CPComm:\r
9010     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9011     break;\r
9012 \r
9013   case CPSock:\r
9014     closesocket(cp->sock);\r
9015     WSACleanup();\r
9016     break;\r
9017 \r
9018   case CPRcmd:\r
9019     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9020     closesocket(cp->sock);\r
9021     closesocket(cp->sock2);\r
9022     WSACleanup();\r
9023     break;\r
9024   }\r
9025   free(cp);\r
9026 }\r
9027 \r
9028 void\r
9029 InterruptChildProcess(ProcRef pr)\r
9030 {\r
9031   ChildProc *cp;\r
9032 \r
9033   cp = (ChildProc *) pr;\r
9034   if (cp == NULL) return;\r
9035   switch (cp->kind) {\r
9036   case CPReal:\r
9037     /* The following doesn't work because the chess program\r
9038        doesn't "have the same console" as WinBoard.  Maybe\r
9039        we could arrange for this even though neither WinBoard\r
9040        nor the chess program uses a console for stdio */\r
9041     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9042     break;\r
9043 \r
9044   case CPComm:\r
9045   case CPSock:\r
9046     /* Can't interrupt */\r
9047     break;\r
9048 \r
9049   case CPRcmd:\r
9050     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9051     break;\r
9052   }\r
9053 }\r
9054 \r
9055 \r
9056 int\r
9057 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9058 {\r
9059   char cmdLine[MSG_SIZ];\r
9060 \r
9061   if (port[0] == NULLCHAR) {\r
9062     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9063   } else {\r
9064     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9065   }\r
9066   return StartChildProcess(cmdLine, "", pr);\r
9067 }\r
9068 \r
9069 \r
9070 /* Code to open TCP sockets */\r
9071 \r
9072 int\r
9073 OpenTCP(char *host, char *port, ProcRef *pr)\r
9074 {\r
9075   ChildProc *cp;\r
9076   int err;\r
9077   SOCKET s;\r
9078   struct sockaddr_in sa, mysa;\r
9079   struct hostent FAR *hp;\r
9080   unsigned short uport;\r
9081   WORD wVersionRequested;\r
9082   WSADATA wsaData;\r
9083 \r
9084   /* Initialize socket DLL */\r
9085   wVersionRequested = MAKEWORD(1, 1);\r
9086   err = WSAStartup(wVersionRequested, &wsaData);\r
9087   if (err != 0) return err;\r
9088 \r
9089   /* Make socket */\r
9090   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9091     err = WSAGetLastError();\r
9092     WSACleanup();\r
9093     return err;\r
9094   }\r
9095 \r
9096   /* Bind local address using (mostly) don't-care values.\r
9097    */\r
9098   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9099   mysa.sin_family = AF_INET;\r
9100   mysa.sin_addr.s_addr = INADDR_ANY;\r
9101   uport = (unsigned short) 0;\r
9102   mysa.sin_port = htons(uport);\r
9103   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9104       == SOCKET_ERROR) {\r
9105     err = WSAGetLastError();\r
9106     WSACleanup();\r
9107     return err;\r
9108   }\r
9109 \r
9110   /* Resolve remote host name */\r
9111   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9112   if (!(hp = gethostbyname(host))) {\r
9113     unsigned int b0, b1, b2, b3;\r
9114 \r
9115     err = WSAGetLastError();\r
9116 \r
9117     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9118       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9119       hp->h_addrtype = AF_INET;\r
9120       hp->h_length = 4;\r
9121       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9122       hp->h_addr_list[0] = (char *) malloc(4);\r
9123       hp->h_addr_list[0][0] = (char) b0;\r
9124       hp->h_addr_list[0][1] = (char) b1;\r
9125       hp->h_addr_list[0][2] = (char) b2;\r
9126       hp->h_addr_list[0][3] = (char) b3;\r
9127     } else {\r
9128       WSACleanup();\r
9129       return err;\r
9130     }\r
9131   }\r
9132   sa.sin_family = hp->h_addrtype;\r
9133   uport = (unsigned short) atoi(port);\r
9134   sa.sin_port = htons(uport);\r
9135   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9136 \r
9137   /* Make connection */\r
9138   if (connect(s, (struct sockaddr *) &sa,\r
9139               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9140     err = WSAGetLastError();\r
9141     WSACleanup();\r
9142     return err;\r
9143   }\r
9144 \r
9145   /* Prepare return value */\r
9146   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9147   cp->kind = CPSock;\r
9148   cp->sock = s;\r
9149   *pr = (ProcRef *) cp;\r
9150 \r
9151   return NO_ERROR;\r
9152 }\r
9153 \r
9154 int\r
9155 OpenCommPort(char *name, ProcRef *pr)\r
9156 {\r
9157   HANDLE h;\r
9158   COMMTIMEOUTS ct;\r
9159   ChildProc *cp;\r
9160   char fullname[MSG_SIZ];\r
9161 \r
9162   if (*name != '\\')\r
9163     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9164   else\r
9165     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9166 \r
9167   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9168                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9169   if (h == (HANDLE) -1) {\r
9170     return GetLastError();\r
9171   }\r
9172   hCommPort = h;\r
9173 \r
9174   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9175 \r
9176   /* Accumulate characters until a 100ms pause, then parse */\r
9177   ct.ReadIntervalTimeout = 100;\r
9178   ct.ReadTotalTimeoutMultiplier = 0;\r
9179   ct.ReadTotalTimeoutConstant = 0;\r
9180   ct.WriteTotalTimeoutMultiplier = 0;\r
9181   ct.WriteTotalTimeoutConstant = 0;\r
9182   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9183 \r
9184   /* Prepare return value */\r
9185   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9186   cp->kind = CPComm;\r
9187   cp->hFrom = h;\r
9188   cp->hTo = h;\r
9189   *pr = (ProcRef *) cp;\r
9190 \r
9191   return NO_ERROR;\r
9192 }\r
9193 \r
9194 int\r
9195 OpenLoopback(ProcRef *pr)\r
9196 {\r
9197   DisplayFatalError(_("Not implemented"), 0, 1);\r
9198   return NO_ERROR;\r
9199 }\r
9200 \r
9201 \r
9202 int\r
9203 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9204 {\r
9205   ChildProc *cp;\r
9206   int err;\r
9207   SOCKET s, s2, s3;\r
9208   struct sockaddr_in sa, mysa;\r
9209   struct hostent FAR *hp;\r
9210   unsigned short uport;\r
9211   WORD wVersionRequested;\r
9212   WSADATA wsaData;\r
9213   int fromPort;\r
9214   char stderrPortStr[MSG_SIZ];\r
9215 \r
9216   /* Initialize socket DLL */\r
9217   wVersionRequested = MAKEWORD(1, 1);\r
9218   err = WSAStartup(wVersionRequested, &wsaData);\r
9219   if (err != 0) return err;\r
9220 \r
9221   /* Resolve remote host name */\r
9222   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9223   if (!(hp = gethostbyname(host))) {\r
9224     unsigned int b0, b1, b2, b3;\r
9225 \r
9226     err = WSAGetLastError();\r
9227 \r
9228     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9229       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9230       hp->h_addrtype = AF_INET;\r
9231       hp->h_length = 4;\r
9232       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9233       hp->h_addr_list[0] = (char *) malloc(4);\r
9234       hp->h_addr_list[0][0] = (char) b0;\r
9235       hp->h_addr_list[0][1] = (char) b1;\r
9236       hp->h_addr_list[0][2] = (char) b2;\r
9237       hp->h_addr_list[0][3] = (char) b3;\r
9238     } else {\r
9239       WSACleanup();\r
9240       return err;\r
9241     }\r
9242   }\r
9243   sa.sin_family = hp->h_addrtype;\r
9244   uport = (unsigned short) 514;\r
9245   sa.sin_port = htons(uport);\r
9246   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9247 \r
9248   /* Bind local socket to unused "privileged" port address\r
9249    */\r
9250   s = INVALID_SOCKET;\r
9251   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9252   mysa.sin_family = AF_INET;\r
9253   mysa.sin_addr.s_addr = INADDR_ANY;\r
9254   for (fromPort = 1023;; fromPort--) {\r
9255     if (fromPort < 0) {\r
9256       WSACleanup();\r
9257       return WSAEADDRINUSE;\r
9258     }\r
9259     if (s == INVALID_SOCKET) {\r
9260       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9261         err = WSAGetLastError();\r
9262         WSACleanup();\r
9263         return err;\r
9264       }\r
9265     }\r
9266     uport = (unsigned short) fromPort;\r
9267     mysa.sin_port = htons(uport);\r
9268     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9269         == SOCKET_ERROR) {\r
9270       err = WSAGetLastError();\r
9271       if (err == WSAEADDRINUSE) continue;\r
9272       WSACleanup();\r
9273       return err;\r
9274     }\r
9275     if (connect(s, (struct sockaddr *) &sa,\r
9276       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9277       err = WSAGetLastError();\r
9278       if (err == WSAEADDRINUSE) {\r
9279         closesocket(s);\r
9280         s = -1;\r
9281         continue;\r
9282       }\r
9283       WSACleanup();\r
9284       return err;\r
9285     }\r
9286     break;\r
9287   }\r
9288 \r
9289   /* Bind stderr local socket to unused "privileged" port address\r
9290    */\r
9291   s2 = INVALID_SOCKET;\r
9292   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9293   mysa.sin_family = AF_INET;\r
9294   mysa.sin_addr.s_addr = INADDR_ANY;\r
9295   for (fromPort = 1023;; fromPort--) {\r
9296     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9297     if (fromPort < 0) {\r
9298       (void) closesocket(s);\r
9299       WSACleanup();\r
9300       return WSAEADDRINUSE;\r
9301     }\r
9302     if (s2 == INVALID_SOCKET) {\r
9303       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9304         err = WSAGetLastError();\r
9305         closesocket(s);\r
9306         WSACleanup();\r
9307         return err;\r
9308       }\r
9309     }\r
9310     uport = (unsigned short) fromPort;\r
9311     mysa.sin_port = htons(uport);\r
9312     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9313         == SOCKET_ERROR) {\r
9314       err = WSAGetLastError();\r
9315       if (err == WSAEADDRINUSE) continue;\r
9316       (void) closesocket(s);\r
9317       WSACleanup();\r
9318       return err;\r
9319     }\r
9320     if (listen(s2, 1) == SOCKET_ERROR) {\r
9321       err = WSAGetLastError();\r
9322       if (err == WSAEADDRINUSE) {\r
9323         closesocket(s2);\r
9324         s2 = INVALID_SOCKET;\r
9325         continue;\r
9326       }\r
9327       (void) closesocket(s);\r
9328       (void) closesocket(s2);\r
9329       WSACleanup();\r
9330       return err;\r
9331     }\r
9332     break;\r
9333   }\r
9334   prevStderrPort = fromPort; // remember port used\r
9335   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9336 \r
9337   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9338     err = WSAGetLastError();\r
9339     (void) closesocket(s);\r
9340     (void) closesocket(s2);\r
9341     WSACleanup();\r
9342     return err;\r
9343   }\r
9344 \r
9345   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9346     err = WSAGetLastError();\r
9347     (void) closesocket(s);\r
9348     (void) closesocket(s2);\r
9349     WSACleanup();\r
9350     return err;\r
9351   }\r
9352   if (*user == NULLCHAR) user = UserName();\r
9353   if (send(s, user, strlen(user) + 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   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9361     err = WSAGetLastError();\r
9362     (void) closesocket(s);\r
9363     (void) closesocket(s2);\r
9364     WSACleanup();\r
9365     return err;\r
9366   }\r
9367 \r
9368   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9369     err = WSAGetLastError();\r
9370     (void) closesocket(s);\r
9371     (void) closesocket(s2);\r
9372     WSACleanup();\r
9373     return err;\r
9374   }\r
9375   (void) closesocket(s2);  /* Stop listening */\r
9376 \r
9377   /* Prepare return value */\r
9378   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9379   cp->kind = CPRcmd;\r
9380   cp->sock = s;\r
9381   cp->sock2 = s3;\r
9382   *pr = (ProcRef *) cp;\r
9383 \r
9384   return NO_ERROR;\r
9385 }\r
9386 \r
9387 \r
9388 InputSourceRef\r
9389 AddInputSource(ProcRef pr, int lineByLine,\r
9390                InputCallback func, VOIDSTAR closure)\r
9391 {\r
9392   InputSource *is, *is2 = NULL;\r
9393   ChildProc *cp = (ChildProc *) pr;\r
9394 \r
9395   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9396   is->lineByLine = lineByLine;\r
9397   is->func = func;\r
9398   is->closure = closure;\r
9399   is->second = NULL;\r
9400   is->next = is->buf;\r
9401   if (pr == NoProc) {\r
9402     is->kind = CPReal;\r
9403     consoleInputSource = is;\r
9404   } else {\r
9405     is->kind = cp->kind;\r
9406     /* \r
9407         [AS] Try to avoid a race condition if the thread is given control too early:\r
9408         we create all threads suspended so that the is->hThread variable can be\r
9409         safely assigned, then let the threads start with ResumeThread.\r
9410     */\r
9411     switch (cp->kind) {\r
9412     case CPReal:\r
9413       is->hFile = cp->hFrom;\r
9414       cp->hFrom = NULL; /* now owned by InputThread */\r
9415       is->hThread =\r
9416         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9417                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9418       break;\r
9419 \r
9420     case CPComm:\r
9421       is->hFile = cp->hFrom;\r
9422       cp->hFrom = NULL; /* now owned by InputThread */\r
9423       is->hThread =\r
9424         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9425                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9426       break;\r
9427 \r
9428     case CPSock:\r
9429       is->sock = cp->sock;\r
9430       is->hThread =\r
9431         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9432                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9433       break;\r
9434 \r
9435     case CPRcmd:\r
9436       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9437       *is2 = *is;\r
9438       is->sock = cp->sock;\r
9439       is->second = is2;\r
9440       is2->sock = cp->sock2;\r
9441       is2->second = is2;\r
9442       is->hThread =\r
9443         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9444                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9445       is2->hThread =\r
9446         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9447                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9448       break;\r
9449     }\r
9450 \r
9451     if( is->hThread != NULL ) {\r
9452         ResumeThread( is->hThread );\r
9453     }\r
9454 \r
9455     if( is2 != NULL && is2->hThread != NULL ) {\r
9456         ResumeThread( is2->hThread );\r
9457     }\r
9458   }\r
9459 \r
9460   return (InputSourceRef) is;\r
9461 }\r
9462 \r
9463 void\r
9464 RemoveInputSource(InputSourceRef isr)\r
9465 {\r
9466   InputSource *is;\r
9467 \r
9468   is = (InputSource *) isr;\r
9469   is->hThread = NULL;  /* tell thread to stop */\r
9470   CloseHandle(is->hThread);\r
9471   if (is->second != NULL) {\r
9472     is->second->hThread = NULL;\r
9473     CloseHandle(is->second->hThread);\r
9474   }\r
9475 }\r
9476 \r
9477 int no_wrap(char *message, int count)\r
9478 {\r
9479     ConsoleOutput(message, count, FALSE);\r
9480     return count;\r
9481 }\r
9482 \r
9483 int\r
9484 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9485 {\r
9486   DWORD dOutCount;\r
9487   int outCount = SOCKET_ERROR;\r
9488   ChildProc *cp = (ChildProc *) pr;\r
9489   static OVERLAPPED ovl;\r
9490   static int line = 0;\r
9491 \r
9492   if (pr == NoProc)\r
9493   {\r
9494     if (appData.noJoin || !appData.useInternalWrap)\r
9495       return no_wrap(message, count);\r
9496     else\r
9497     {\r
9498       int width = get_term_width();\r
9499       int len = wrap(NULL, message, count, width, &line);\r
9500       char *msg = malloc(len);\r
9501       int dbgchk;\r
9502 \r
9503       if (!msg)\r
9504         return no_wrap(message, count);\r
9505       else\r
9506       {\r
9507         dbgchk = wrap(msg, message, count, width, &line);\r
9508         if (dbgchk != len && appData.debugMode)\r
9509             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9510         ConsoleOutput(msg, len, FALSE);\r
9511         free(msg);\r
9512         return len;\r
9513       }\r
9514     }\r
9515   }\r
9516 \r
9517   if (ovl.hEvent == NULL) {\r
9518     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9519   }\r
9520   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9521 \r
9522   switch (cp->kind) {\r
9523   case CPSock:\r
9524   case CPRcmd:\r
9525     outCount = send(cp->sock, message, count, 0);\r
9526     if (outCount == SOCKET_ERROR) {\r
9527       *outError = WSAGetLastError();\r
9528     } else {\r
9529       *outError = NO_ERROR;\r
9530     }\r
9531     break;\r
9532 \r
9533   case CPReal:\r
9534     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9535                   &dOutCount, NULL)) {\r
9536       *outError = NO_ERROR;\r
9537       outCount = (int) dOutCount;\r
9538     } else {\r
9539       *outError = GetLastError();\r
9540     }\r
9541     break;\r
9542 \r
9543   case CPComm:\r
9544     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9545                             &dOutCount, &ovl);\r
9546     if (*outError == NO_ERROR) {\r
9547       outCount = (int) dOutCount;\r
9548     }\r
9549     break;\r
9550   }\r
9551   return outCount;\r
9552 }\r
9553 \r
9554 int\r
9555 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9556                        long msdelay)\r
9557 {\r
9558   /* Ignore delay, not implemented for WinBoard */\r
9559   return OutputToProcess(pr, message, count, outError);\r
9560 }\r
9561 \r
9562 \r
9563 void\r
9564 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9565                         char *buf, int count, int error)\r
9566 {\r
9567   DisplayFatalError(_("Not implemented"), 0, 1);\r
9568 }\r
9569 \r
9570 /* see wgamelist.c for Game List functions */\r
9571 /* see wedittags.c for Edit Tags functions */\r
9572 \r
9573 \r
9574 VOID\r
9575 ICSInitScript()\r
9576 {\r
9577   FILE *f;\r
9578   char buf[MSG_SIZ];\r
9579   char *dummy;\r
9580 \r
9581   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9582     f = fopen(buf, "r");\r
9583     if (f != NULL) {\r
9584       ProcessICSInitScript(f);\r
9585       fclose(f);\r
9586     }\r
9587   }\r
9588 }\r
9589 \r
9590 \r
9591 VOID\r
9592 StartAnalysisClock()\r
9593 {\r
9594   if (analysisTimerEvent) return;\r
9595   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9596                                         (UINT) 2000, NULL);\r
9597 }\r
9598 \r
9599 VOID\r
9600 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9601 {\r
9602   highlightInfo.sq[0].x = fromX;\r
9603   highlightInfo.sq[0].y = fromY;\r
9604   highlightInfo.sq[1].x = toX;\r
9605   highlightInfo.sq[1].y = toY;\r
9606 }\r
9607 \r
9608 VOID\r
9609 ClearHighlights()\r
9610 {\r
9611   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9612     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9613 }\r
9614 \r
9615 VOID\r
9616 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9617 {\r
9618   premoveHighlightInfo.sq[0].x = fromX;\r
9619   premoveHighlightInfo.sq[0].y = fromY;\r
9620   premoveHighlightInfo.sq[1].x = toX;\r
9621   premoveHighlightInfo.sq[1].y = toY;\r
9622 }\r
9623 \r
9624 VOID\r
9625 ClearPremoveHighlights()\r
9626 {\r
9627   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9628     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9629 }\r
9630 \r
9631 VOID\r
9632 ShutDownFrontEnd()\r
9633 {\r
9634   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9635   DeleteClipboardTempFiles();\r
9636 }\r
9637 \r
9638 void\r
9639 BoardToTop()\r
9640 {\r
9641     if (IsIconic(hwndMain))\r
9642       ShowWindow(hwndMain, SW_RESTORE);\r
9643 \r
9644     SetActiveWindow(hwndMain);\r
9645 }\r
9646 \r
9647 /*\r
9648  * Prototypes for animation support routines\r
9649  */\r
9650 static void ScreenSquare(int column, int row, POINT * pt);\r
9651 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9652      POINT frames[], int * nFrames);\r
9653 \r
9654 \r
9655 #define kFactor 4\r
9656 \r
9657 void\r
9658 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9659 {       // [HGM] atomic: animate blast wave\r
9660         int i;\r
9661 \r
9662         explodeInfo.fromX = fromX;\r
9663         explodeInfo.fromY = fromY;\r
9664         explodeInfo.toX = toX;\r
9665         explodeInfo.toY = toY;\r
9666         for(i=1; i<4*kFactor; i++) {\r
9667             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9668             DrawPosition(FALSE, board);\r
9669             Sleep(appData.animSpeed);\r
9670         }\r
9671         explodeInfo.radius = 0;\r
9672         DrawPosition(TRUE, board);\r
9673 }\r
9674 \r
9675 void\r
9676 AnimateMove(board, fromX, fromY, toX, toY)\r
9677      Board board;\r
9678      int fromX;\r
9679      int fromY;\r
9680      int toX;\r
9681      int toY;\r
9682 {\r
9683   ChessSquare piece;\r
9684   POINT start, finish, mid;\r
9685   POINT frames[kFactor * 2 + 1];\r
9686   int nFrames, n;\r
9687 \r
9688   if (!appData.animate) return;\r
9689   if (doingSizing) return;\r
9690   if (fromY < 0 || fromX < 0) return;\r
9691   piece = board[fromY][fromX];\r
9692   if (piece >= EmptySquare) return;\r
9693 \r
9694   ScreenSquare(fromX, fromY, &start);\r
9695   ScreenSquare(toX, toY, &finish);\r
9696 \r
9697   /* All moves except knight jumps move in straight line */\r
9698   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9699     mid.x = start.x + (finish.x - start.x) / 2;\r
9700     mid.y = start.y + (finish.y - start.y) / 2;\r
9701   } else {\r
9702     /* Knight: make straight movement then diagonal */\r
9703     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9704        mid.x = start.x + (finish.x - start.x) / 2;\r
9705        mid.y = start.y;\r
9706      } else {\r
9707        mid.x = start.x;\r
9708        mid.y = start.y + (finish.y - start.y) / 2;\r
9709      }\r
9710   }\r
9711   \r
9712   /* Don't use as many frames for very short moves */\r
9713   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9714     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9715   else\r
9716     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9717 \r
9718   animInfo.from.x = fromX;\r
9719   animInfo.from.y = fromY;\r
9720   animInfo.to.x = toX;\r
9721   animInfo.to.y = toY;\r
9722   animInfo.lastpos = start;\r
9723   animInfo.piece = piece;\r
9724   for (n = 0; n < nFrames; n++) {\r
9725     animInfo.pos = frames[n];\r
9726     DrawPosition(FALSE, NULL);\r
9727     animInfo.lastpos = animInfo.pos;\r
9728     Sleep(appData.animSpeed);\r
9729   }\r
9730   animInfo.pos = finish;\r
9731   DrawPosition(FALSE, NULL);\r
9732   animInfo.piece = EmptySquare;\r
9733   Explode(board, fromX, fromY, toX, toY);\r
9734 }\r
9735 \r
9736 /*      Convert board position to corner of screen rect and color       */\r
9737 \r
9738 static void\r
9739 ScreenSquare(column, row, pt)\r
9740      int column; int row; POINT * pt;\r
9741 {\r
9742   if (flipView) {\r
9743     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9744     pt->y = lineGap + row * (squareSize + lineGap);\r
9745   } else {\r
9746     pt->x = lineGap + column * (squareSize + lineGap);\r
9747     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9748   }\r
9749 }\r
9750 \r
9751 /*      Generate a series of frame coords from start->mid->finish.\r
9752         The movement rate doubles until the half way point is\r
9753         reached, then halves back down to the final destination,\r
9754         which gives a nice slow in/out effect. The algorithmn\r
9755         may seem to generate too many intermediates for short\r
9756         moves, but remember that the purpose is to attract the\r
9757         viewers attention to the piece about to be moved and\r
9758         then to where it ends up. Too few frames would be less\r
9759         noticeable.                                             */\r
9760 \r
9761 static void\r
9762 Tween(start, mid, finish, factor, frames, nFrames)\r
9763      POINT * start; POINT * mid;\r
9764      POINT * finish; int factor;\r
9765      POINT frames[]; int * nFrames;\r
9766 {\r
9767   int n, fraction = 1, count = 0;\r
9768 \r
9769   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9770   for (n = 0; n < factor; n++)\r
9771     fraction *= 2;\r
9772   for (n = 0; n < factor; n++) {\r
9773     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9774     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9775     count ++;\r
9776     fraction = fraction / 2;\r
9777   }\r
9778   \r
9779   /* Midpoint */\r
9780   frames[count] = *mid;\r
9781   count ++;\r
9782   \r
9783   /* Slow out, stepping 1/2, then 1/4, ... */\r
9784   fraction = 2;\r
9785   for (n = 0; n < factor; n++) {\r
9786     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9787     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9788     count ++;\r
9789     fraction = fraction * 2;\r
9790   }\r
9791   *nFrames = count;\r
9792 }\r
9793 \r
9794 void\r
9795 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9796 {\r
9797     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9798 \r
9799     EvalGraphSet( first, last, current, pvInfoList );\r
9800 }\r
9801 \r
9802 void\r
9803 SettingsPopUp(ChessProgramState *cps)\r
9804 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9805       EngineOptionsPopup(savedHwnd, cps);\r
9806 }\r