Make sweep-select promotions work 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, 2011 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 \r
77 #if __GNUC__\r
78 #include <errno.h>\r
79 #include <string.h>\r
80 #endif\r
81 \r
82 #include "common.h"\r
83 #include "frontend.h"\r
84 #include "backend.h"\r
85 #include "winboard.h"\r
86 #include "moves.h"\r
87 #include "wclipbrd.h"\r
88 #include "woptions.h"\r
89 #include "wsockerr.h"\r
90 #include "defaults.h"\r
91 #include "help.h"\r
92 #include "wsnap.h"\r
93 \r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
95 \r
96   int myrandom(void);\r
97   void mysrandom(unsigned int seed);\r
98 \r
99 extern int whiteFlag, blackFlag;\r
100 Boolean flipClock = FALSE;\r
101 extern HANDLE chatHandle[];\r
102 extern int ics_type;\r
103 \r
104 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
105 VOID NewVariantPopup(HWND hwnd);\r
106 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
107                    /*char*/int promoChar));\r
108 void DisplayMove P((int moveNumber));\r
109 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
110 void ChatPopUp P((char *s));\r
111 typedef struct {\r
112   ChessSquare piece;  \r
113   POINT pos;      /* window coordinates of current pos */\r
114   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
115   POINT from;     /* board coordinates of the piece's orig pos */\r
116   POINT to;       /* board coordinates of the piece's new pos */\r
117 } AnimInfo;\r
118 \r
119 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
120 \r
121 typedef struct {\r
122   POINT start;    /* window coordinates of start pos */\r
123   POINT pos;      /* window coordinates of current pos */\r
124   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
125   POINT from;     /* board coordinates of the piece's orig pos */\r
126 } DragInfo;\r
127 \r
128 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
129 \r
130 typedef struct {\r
131   POINT sq[2];    /* board coordinates of from, to squares */\r
132 } HighlightInfo;\r
133 \r
134 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
135 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
136 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
137 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
138 \r
139 typedef struct { // [HGM] atomic\r
140   int fromX, fromY, toX, toY, radius;\r
141 } ExplodeInfo;\r
142 \r
143 static ExplodeInfo explodeInfo;\r
144 \r
145 /* Window class names */\r
146 char szAppName[] = "WinBoard";\r
147 char szConsoleName[] = "WBConsole";\r
148 \r
149 /* Title bar text */\r
150 char szTitle[] = "WinBoard";\r
151 char szConsoleTitle[] = "I C S Interaction";\r
152 \r
153 char *programName;\r
154 char *settingsFileName;\r
155 Boolean saveSettingsOnExit;\r
156 char installDir[MSG_SIZ];\r
157 int errorExitStatus;\r
158 \r
159 BoardSize boardSize;\r
160 Boolean chessProgram;\r
161 //static int boardX, boardY;\r
162 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
163 int squareSize, lineGap, minorSize;\r
164 static int winW, winH;\r
165 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
166 static int logoHeight = 0;\r
167 static char messageText[MESSAGE_TEXT_MAX];\r
168 static int clockTimerEvent = 0;\r
169 static int loadGameTimerEvent = 0;\r
170 static int analysisTimerEvent = 0;\r
171 static DelayedEventCallback delayedTimerCallback;\r
172 static int delayedTimerEvent = 0;\r
173 static int buttonCount = 2;\r
174 char *icsTextMenuString;\r
175 char *icsNames;\r
176 char *firstChessProgramNames;\r
177 char *secondChessProgramNames;\r
178 \r
179 #define PALETTESIZE 256\r
180 \r
181 HINSTANCE hInst;          /* current instance */\r
182 Boolean alwaysOnTop = FALSE;\r
183 RECT boardRect;\r
184 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
185   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
186 HPALETTE hPal;\r
187 ColorClass currentColorClass;\r
188 \r
189 static HWND savedHwnd;\r
190 HWND hCommPort = NULL;    /* currently open comm port */\r
191 static HWND hwndPause;    /* pause button */\r
192 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
193 static HBRUSH lightSquareBrush, darkSquareBrush,\r
194   blackSquareBrush, /* [HGM] for band between board and holdings */\r
195   explodeBrush,     /* [HGM] atomic */\r
196   markerBrush,      /* [HGM] markers */\r
197   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
198 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
199 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
200 static HPEN gridPen = NULL;\r
201 static HPEN highlightPen = NULL;\r
202 static HPEN premovePen = NULL;\r
203 static NPLOGPALETTE pLogPal;\r
204 static BOOL paletteChanged = FALSE;\r
205 static HICON iconWhite, iconBlack, iconCurrent;\r
206 static int doingSizing = FALSE;\r
207 static int lastSizing = 0;\r
208 static int prevStderrPort;\r
209 static HBITMAP userLogo;\r
210 \r
211 static HBITMAP liteBackTexture = NULL;\r
212 static HBITMAP darkBackTexture = NULL;\r
213 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
214 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
215 static int backTextureSquareSize = 0;\r
216 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
217 \r
218 #if __GNUC__ && !defined(_winmajor)\r
219 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
220 #else\r
221 #if defined(_winmajor)\r
222 #define oldDialog (_winmajor < 4)\r
223 #else\r
224 #define oldDialog 0\r
225 #endif\r
226 #endif\r
227 \r
228 #define INTERNATIONAL\r
229 \r
230 #ifdef INTERNATIONAL\r
231 #  define _(s) T_(s)\r
232 #  define N_(s) s\r
233 #else\r
234 #  define _(s) s\r
235 #  define N_(s) s\r
236 #  define T_(s) s\r
237 #  define Translate(x, y)\r
238 #  define LoadLanguageFile(s)\r
239 #endif\r
240 \r
241 #ifdef INTERNATIONAL\r
242 \r
243 Boolean barbaric; // flag indicating if translation is needed\r
244 \r
245 // list of item numbers used in each dialog (used to alter language at run time)\r
246 \r
247 #define ABOUTBOX -1  /* not sure why these are needed */\r
248 #define ABOUTBOX2 -1\r
249 \r
250 int dialogItems[][40] = {\r
251 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
252 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
253   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
254 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, IDOK, IDCANCEL }, \r
255 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
256   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
257 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
258 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
259   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
260 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
261 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
262   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
263 { ABOUTBOX2, IDC_ChessBoard }, \r
264 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
265   OPT_GameListClose, IDC_GameListDoFilter }, \r
266 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
267 { DLG_Error, IDOK }, \r
268 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
269   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
270 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
271 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
272   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
273   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
274 { DLG_IndexNumber, IDC_Index }, \r
275 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
276 { DLG_TypeInName, IDOK, IDCANCEL }, \r
277 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
278   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
279 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
280   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
281   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
282   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
283   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
284   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
285   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
286 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
287   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
288   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
289   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
290   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
291   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
292   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
293   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
294   GPB_General, GPB_Alarm }, \r
295 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
296   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
297   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
298   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
299   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
300   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
301   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
302   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size }, \r
303 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
304   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
305   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
306   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
307   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
308   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
309   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
310   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
311   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
312 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
313   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
314   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,\r
315   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
316 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
317 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
318   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
319 { DLG_MoveHistory }, \r
320 { DLG_EvalGraph }, \r
321 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
322 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
323 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
324   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
325   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
326   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
327 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
328   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
329   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
330 { 0 }\r
331 };\r
332 \r
333 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
334 static int lastChecked;\r
335 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
336 extern int tinyLayout;\r
337 extern char * menuBarText[][10];\r
338 \r
339 void\r
340 LoadLanguageFile(char *name)\r
341 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
342     FILE *f;\r
343     int i=0, j=0, n=0, k;\r
344     char buf[MSG_SIZ];\r
345 \r
346     if(!name || name[0] == NULLCHAR) return;\r
347       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
348     appData.language = oldLanguage;\r
349     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
350     if((f = fopen(buf, "r")) == NULL) return;\r
351     while((k = fgetc(f)) != EOF) {\r
352         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
353         languageBuf[i] = k;\r
354         if(k == '\n') {\r
355             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
356                 char *p;\r
357                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
358                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
359                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
360                         english[j] = languageBuf + n + 1; *p = 0;\r
361                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
362 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
363                     }\r
364                 }\r
365             }\r
366             n = i + 1;\r
367         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
368             switch(k) {\r
369               case 'n': k = '\n'; break;\r
370               case 'r': k = '\r'; break;\r
371               case 't': k = '\t'; break;\r
372             }\r
373             languageBuf[--i] = k;\r
374         }\r
375         i++;\r
376     }\r
377     fclose(f);\r
378     barbaric = (j != 0);\r
379     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
380 }\r
381 \r
382 char *\r
383 T_(char *s)\r
384 {   // return the translation of the given string\r
385     // efficiency can be improved a lot...\r
386     int i=0;\r
387 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
388     if(!barbaric) return s;\r
389     if(!s) return ""; // sanity\r
390     while(english[i]) {\r
391         if(!strcmp(s, english[i])) return foreign[i];\r
392         i++;\r
393     }\r
394     return s;\r
395 }\r
396 \r
397 void\r
398 Translate(HWND hDlg, int dialogID)\r
399 {   // translate all text items in the given dialog\r
400     int i=0, j, k;\r
401     char buf[MSG_SIZ], *s;\r
402     if(!barbaric) return;\r
403     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
404     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
405     GetWindowText( hDlg, buf, MSG_SIZ );\r
406     s = T_(buf);\r
407     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
408     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
409         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
410         if(strlen(buf) == 0) continue;\r
411         s = T_(buf);\r
412         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
413     }\r
414 }\r
415 \r
416 HMENU\r
417 TranslateOneMenu(int i, HMENU subMenu)\r
418 {\r
419     int j;\r
420     static MENUITEMINFO info;\r
421 \r
422     info.cbSize = sizeof(MENUITEMINFO);\r
423     info.fMask = MIIM_STATE | MIIM_TYPE;\r
424           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
425             char buf[MSG_SIZ];\r
426             info.dwTypeData = buf;\r
427             info.cch = sizeof(buf);\r
428             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
429             if(i < 10) {
430                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
431                 else menuText[i][j] = strdup(buf); // remember original on first change\r
432             }\r
433             if(buf[0] == NULLCHAR) continue;\r
434             info.dwTypeData = T_(buf);\r
435             info.cch = strlen(buf)+1;\r
436             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
437           }\r
438     return subMenu;\r
439 }\r
440 \r
441 void\r
442 TranslateMenus(int addLanguage)\r
443 {\r
444     int i;\r
445     WIN32_FIND_DATA fileData;\r
446     HANDLE hFind;\r
447 #define IDM_English 1970\r
448     if(1) {\r
449         HMENU mainMenu = GetMenu(hwndMain);\r
450         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
451           HMENU subMenu = GetSubMenu(mainMenu, i);\r
452           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
453                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
454           TranslateOneMenu(i, subMenu);\r
455         }\r
456         DrawMenuBar(hwndMain);\r
457     }\r
458 \r
459     if(!addLanguage) return;\r
460     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
461         HMENU mainMenu = GetMenu(hwndMain);\r
462         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
463         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
464         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
465         i = 0; lastChecked = IDM_English;\r
466         do {\r
467             char *p, *q = fileData.cFileName;\r
468             int checkFlag = MF_UNCHECKED;\r
469             languageFile[i] = strdup(q);\r
470             if(barbaric && !strcmp(oldLanguage, q)) {\r
471                 checkFlag = MF_CHECKED;\r
472                 lastChecked = IDM_English + i + 1;\r
473                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
474             }\r
475             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
476             p = strstr(fileData.cFileName, ".lng");\r
477             if(p) *p = 0;\r
478             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
479         } while(FindNextFile(hFind, &fileData));\r
480         FindClose(hFind);\r
481     }\r
482 }\r
483 \r
484 #endif\r
485 \r
486 typedef struct {\r
487   char *name;\r
488   int squareSize;\r
489   int lineGap;\r
490   int smallLayout;\r
491   int tinyLayout;\r
492   int cliWidth, cliHeight;\r
493 } SizeInfo;\r
494 \r
495 SizeInfo sizeInfo[] = \r
496 {\r
497   { "tiny",     21, 0, 1, 1, 0, 0 },\r
498   { "teeny",    25, 1, 1, 1, 0, 0 },\r
499   { "dinky",    29, 1, 1, 1, 0, 0 },\r
500   { "petite",   33, 1, 1, 1, 0, 0 },\r
501   { "slim",     37, 2, 1, 0, 0, 0 },\r
502   { "small",    40, 2, 1, 0, 0, 0 },\r
503   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
504   { "middling", 49, 2, 0, 0, 0, 0 },\r
505   { "average",  54, 2, 0, 0, 0, 0 },\r
506   { "moderate", 58, 3, 0, 0, 0, 0 },\r
507   { "medium",   64, 3, 0, 0, 0, 0 },\r
508   { "bulky",    72, 3, 0, 0, 0, 0 },\r
509   { "large",    80, 3, 0, 0, 0, 0 },\r
510   { "big",      87, 3, 0, 0, 0, 0 },\r
511   { "huge",     95, 3, 0, 0, 0, 0 },\r
512   { "giant",    108, 3, 0, 0, 0, 0 },\r
513   { "colossal", 116, 4, 0, 0, 0, 0 },\r
514   { "titanic",  129, 4, 0, 0, 0, 0 },\r
515   { NULL, 0, 0, 0, 0, 0, 0 }\r
516 };\r
517 \r
518 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
519 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
520 {\r
521   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL) },\r
522   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL) },\r
523   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL) },\r
524   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL) },\r
525   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL) },\r
526   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL) },\r
527   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL) },\r
528   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL) },\r
529   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL) },\r
530   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL) },\r
531   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL) },\r
532   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL) },\r
533   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL) },\r
534   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL) },\r
535   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL) },\r
536   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL) },\r
537   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL) },\r
538   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL) },\r
539 };\r
540 \r
541 MyFont *font[NUM_SIZES][NUM_FONTS];\r
542 \r
543 typedef struct {\r
544   char *label;\r
545   int id;\r
546   HWND hwnd;\r
547   WNDPROC wndproc;\r
548 } MyButtonDesc;\r
549 \r
550 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
551 #define N_BUTTONS 5\r
552 \r
553 MyButtonDesc buttonDesc[N_BUTTONS] =\r
554 {\r
555   {"<<", IDM_ToStart, NULL, NULL},\r
556   {"<", IDM_Backward, NULL, NULL},\r
557   {"P", IDM_Pause, NULL, NULL},\r
558   {">", IDM_Forward, NULL, NULL},\r
559   {">>", IDM_ToEnd, NULL, NULL},\r
560 };\r
561 \r
562 int tinyLayout = 0, smallLayout = 0;\r
563 #define MENU_BAR_ITEMS 9\r
564 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
565   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
566   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
567 };\r
568 \r
569 \r
570 MySound sounds[(int)NSoundClasses];\r
571 MyTextAttribs textAttribs[(int)NColorClasses];\r
572 \r
573 MyColorizeAttribs colorizeAttribs[] = {\r
574   { (COLORREF)0, 0, N_("Shout Text") },\r
575   { (COLORREF)0, 0, N_("SShout/CShout") },\r
576   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
577   { (COLORREF)0, 0, N_("Channel Text") },\r
578   { (COLORREF)0, 0, N_("Kibitz Text") },\r
579   { (COLORREF)0, 0, N_("Tell Text") },\r
580   { (COLORREF)0, 0, N_("Challenge Text") },\r
581   { (COLORREF)0, 0, N_("Request Text") },\r
582   { (COLORREF)0, 0, N_("Seek Text") },\r
583   { (COLORREF)0, 0, N_("Normal Text") },\r
584   { (COLORREF)0, 0, N_("None") }\r
585 };\r
586 \r
587 \r
588 \r
589 static char *commentTitle;\r
590 static char *commentText;\r
591 static int commentIndex;\r
592 static Boolean editComment = FALSE;\r
593 \r
594 \r
595 char errorTitle[MSG_SIZ];\r
596 char errorMessage[2*MSG_SIZ];\r
597 HWND errorDialog = NULL;\r
598 BOOLEAN moveErrorMessageUp = FALSE;\r
599 BOOLEAN consoleEcho = TRUE;\r
600 CHARFORMAT consoleCF;\r
601 COLORREF consoleBackgroundColor;\r
602 \r
603 char *programVersion;\r
604 \r
605 #define CPReal 1\r
606 #define CPComm 2\r
607 #define CPSock 3\r
608 #define CPRcmd 4\r
609 typedef int CPKind;\r
610 \r
611 typedef struct {\r
612   CPKind kind;\r
613   HANDLE hProcess;\r
614   DWORD pid;\r
615   HANDLE hTo;\r
616   HANDLE hFrom;\r
617   SOCKET sock;\r
618   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
619 } ChildProc;\r
620 \r
621 #define INPUT_SOURCE_BUF_SIZE 4096\r
622 \r
623 typedef struct _InputSource {\r
624   CPKind kind;\r
625   HANDLE hFile;\r
626   SOCKET sock;\r
627   int lineByLine;\r
628   HANDLE hThread;\r
629   DWORD id;\r
630   char buf[INPUT_SOURCE_BUF_SIZE];\r
631   char *next;\r
632   DWORD count;\r
633   int error;\r
634   InputCallback func;\r
635   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
636   VOIDSTAR closure;\r
637 } InputSource;\r
638 \r
639 InputSource *consoleInputSource;\r
640 \r
641 DCB dcb;\r
642 \r
643 /* forward */\r
644 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
645 VOID ConsoleCreate();\r
646 LRESULT CALLBACK\r
647   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
648 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
649 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
650 VOID ParseCommSettings(char *arg, DCB *dcb);\r
651 LRESULT CALLBACK\r
652   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
653 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
654 void ParseIcsTextMenu(char *icsTextMenuString);\r
655 VOID PopUpMoveDialog(char firstchar);\r
656 VOID PopUpNameDialog(char firstchar);\r
657 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
658 \r
659 /* [AS] */\r
660 int NewGameFRC();\r
661 int GameListOptions();\r
662 \r
663 int dummy; // [HGM] for obsolete args\r
664 \r
665 HWND hwndMain = NULL;        /* root window*/\r
666 HWND hwndConsole = NULL;\r
667 HWND commentDialog = NULL;\r
668 HWND moveHistoryDialog = NULL;\r
669 HWND evalGraphDialog = NULL;\r
670 HWND engineOutputDialog = NULL;\r
671 HWND gameListDialog = NULL;\r
672 HWND editTagsDialog = NULL;\r
673 \r
674 int commentUp = FALSE;\r
675 \r
676 WindowPlacement wpMain;\r
677 WindowPlacement wpConsole;\r
678 WindowPlacement wpComment;\r
679 WindowPlacement wpMoveHistory;\r
680 WindowPlacement wpEvalGraph;\r
681 WindowPlacement wpEngineOutput;\r
682 WindowPlacement wpGameList;\r
683 WindowPlacement wpTags;\r
684 \r
685 VOID EngineOptionsPopup(); // [HGM] settings\r
686 \r
687 VOID GothicPopUp(char *title, VariantClass variant);\r
688 /*\r
689  * Setting "frozen" should disable all user input other than deleting\r
690  * the window.  We do this while engines are initializing themselves.\r
691  */\r
692 static int frozen = 0;\r
693 static int oldMenuItemState[MENU_BAR_ITEMS];\r
694 void FreezeUI()\r
695 {\r
696   HMENU hmenu;\r
697   int i;\r
698 \r
699   if (frozen) return;\r
700   frozen = 1;\r
701   hmenu = GetMenu(hwndMain);\r
702   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
703     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
704   }\r
705   DrawMenuBar(hwndMain);\r
706 }\r
707 \r
708 /* Undo a FreezeUI */\r
709 void ThawUI()\r
710 {\r
711   HMENU hmenu;\r
712   int i;\r
713 \r
714   if (!frozen) return;\r
715   frozen = 0;\r
716   hmenu = GetMenu(hwndMain);\r
717   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
718     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
719   }\r
720   DrawMenuBar(hwndMain);\r
721 }\r
722 \r
723 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
724 \r
725 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
726 #ifdef JAWS\r
727 #include "jaws.c"\r
728 #else\r
729 #define JAWS_INIT\r
730 #define JAWS_ARGS\r
731 #define JAWS_ALT_INTERCEPT\r
732 #define JAWS_KB_NAVIGATION\r
733 #define JAWS_MENU_ITEMS\r
734 #define JAWS_SILENCE\r
735 #define JAWS_REPLAY\r
736 #define JAWS_ACCEL\r
737 #define JAWS_COPYRIGHT\r
738 #define JAWS_DELETE(X) X\r
739 #define SAYMACHINEMOVE()\r
740 #define SAY(X)\r
741 #endif\r
742 \r
743 /*---------------------------------------------------------------------------*\\r
744  *\r
745  * WinMain\r
746  *\r
747 \*---------------------------------------------------------------------------*/\r
748 \r
749 int APIENTRY\r
750 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
751         LPSTR lpCmdLine, int nCmdShow)\r
752 {\r
753   MSG msg;\r
754   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
755 //  INITCOMMONCONTROLSEX ex;\r
756 \r
757   debugFP = stderr;\r
758 \r
759   LoadLibrary("RICHED32.DLL");\r
760   consoleCF.cbSize = sizeof(CHARFORMAT);\r
761 \r
762   if (!InitApplication(hInstance)) {\r
763     return (FALSE);\r
764   }\r
765   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
766     return (FALSE);\r
767   }\r
768 \r
769   JAWS_INIT\r
770   TranslateMenus(1);\r
771 \r
772 //  InitCommonControlsEx(&ex);\r
773   InitCommonControls();\r
774 \r
775   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
776   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
777   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
778 \r
779   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
780 \r
781   while (GetMessage(&msg, /* message structure */\r
782                     NULL, /* handle of window receiving the message */\r
783                     0,    /* lowest message to examine */\r
784                     0))   /* highest message to examine */\r
785     {\r
786 \r
787       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
788         // [HGM] navigate: switch between all windows with tab\r
789         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
790         int i, currentElement = 0;\r
791 \r
792         // first determine what element of the chain we come from (if any)\r
793         if(appData.icsActive) {\r
794             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
795             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
796         }\r
797         if(engineOutputDialog && EngineOutputIsUp()) {\r
798             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
799             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
800         }\r
801         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
802             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
803         }\r
804         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
805         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
806         if(msg.hwnd == e1)                 currentElement = 2; else\r
807         if(msg.hwnd == e2)                 currentElement = 3; else\r
808         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
809         if(msg.hwnd == mh)                currentElement = 4; else\r
810         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
811         if(msg.hwnd == hText)  currentElement = 5; else\r
812         if(msg.hwnd == hInput) currentElement = 6; else\r
813         for (i = 0; i < N_BUTTONS; i++) {\r
814             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
815         }\r
816 \r
817         // determine where to go to\r
818         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
819           do {\r
820             currentElement = (currentElement + direction) % 7;\r
821             switch(currentElement) {\r
822                 case 0:\r
823                   h = hwndMain; break; // passing this case always makes the loop exit\r
824                 case 1:\r
825                   h = buttonDesc[0].hwnd; break; // could be NULL\r
826                 case 2:\r
827                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
828                   h = e1; break;\r
829                 case 3:\r
830                   if(!EngineOutputIsUp()) continue;\r
831                   h = e2; break;\r
832                 case 4:\r
833                   if(!MoveHistoryIsUp()) continue;\r
834                   h = mh; break;\r
835 //              case 6: // input to eval graph does not seem to get here!\r
836 //                if(!EvalGraphIsUp()) continue;\r
837 //                h = evalGraphDialog; break;\r
838                 case 5:\r
839                   if(!appData.icsActive) continue;\r
840                   SAY("display");\r
841                   h = hText; break;\r
842                 case 6:\r
843                   if(!appData.icsActive) continue;\r
844                   SAY("input");\r
845                   h = hInput; break;\r
846             }\r
847           } while(h == 0);\r
848 \r
849           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
850           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
851           SetFocus(h);\r
852 \r
853           continue; // this message now has been processed\r
854         }\r
855       }\r
856 \r
857       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
858           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
859           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
860           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
861           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
862           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
863           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
864           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
865           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
866           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
867         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
868         for(i=0; i<MAX_CHAT; i++) \r
869             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
870                 done = 1; break;\r
871         }\r
872         if(done) continue; // [HGM] chat: end patch\r
873         TranslateMessage(&msg); /* Translates virtual key codes */\r
874         DispatchMessage(&msg);  /* Dispatches message to window */\r
875       }\r
876     }\r
877 \r
878 \r
879   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
880 }\r
881 \r
882 /*---------------------------------------------------------------------------*\\r
883  *\r
884  * Initialization functions\r
885  *\r
886 \*---------------------------------------------------------------------------*/\r
887 \r
888 void\r
889 SetUserLogo()\r
890 {   // update user logo if necessary\r
891     static char oldUserName[MSG_SIZ], *curName;\r
892 \r
893     if(appData.autoLogo) {\r
894           curName = UserName();\r
895           if(strcmp(curName, oldUserName)) {\r
896             snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
897                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
898                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
899                 if(userLogo == NULL)\r
900                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
901           }\r
902     }\r
903 }\r
904 \r
905 BOOL\r
906 InitApplication(HINSTANCE hInstance)\r
907 {\r
908   WNDCLASS wc;\r
909 \r
910   /* Fill in window class structure with parameters that describe the */\r
911   /* main window. */\r
912 \r
913   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
914   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
915   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
916   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
917   wc.hInstance     = hInstance;         /* Owner of this class */\r
918   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
919   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
920   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
921   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
922   wc.lpszClassName = szAppName;                 /* Name to register as */\r
923 \r
924   /* Register the window class and return success/failure code. */\r
925   if (!RegisterClass(&wc)) return FALSE;\r
926 \r
927   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
928   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
929   wc.cbClsExtra    = 0;\r
930   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
931   wc.hInstance     = hInstance;\r
932   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
933   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
934   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
935   wc.lpszMenuName  = NULL;\r
936   wc.lpszClassName = szConsoleName;\r
937 \r
938   if (!RegisterClass(&wc)) return FALSE;\r
939   return TRUE;\r
940 }\r
941 \r
942 \r
943 /* Set by InitInstance, used by EnsureOnScreen */\r
944 int screenHeight, screenWidth;\r
945 \r
946 void\r
947 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
948 {\r
949 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
950   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
951   if (*x > screenWidth - 32) *x = 0;\r
952   if (*y > screenHeight - 32) *y = 0;\r
953   if (*x < minX) *x = minX;\r
954   if (*y < minY) *y = minY;\r
955 }\r
956 \r
957 BOOL\r
958 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
959 {\r
960   HWND hwnd; /* Main window handle. */\r
961   int ibs;\r
962   WINDOWPLACEMENT wp;\r
963   char *filepart;\r
964 \r
965   hInst = hInstance;    /* Store instance handle in our global variable */\r
966   programName = szAppName;\r
967 \r
968   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
969     *filepart = NULLCHAR;\r
970   } else {\r
971     GetCurrentDirectory(MSG_SIZ, installDir);\r
972   }\r
973   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
974   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
975   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
976   /* xboard, and older WinBoards, controlled the move sound with the\r
977      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
978      always turn the option on (so that the backend will call us),\r
979      then let the user turn the sound off by setting it to silence if\r
980      desired.  To accommodate old winboard.ini files saved by old\r
981      versions of WinBoard, we also turn off the sound if the option\r
982      was initially set to false. [HGM] taken out of InitAppData */\r
983   if (!appData.ringBellAfterMoves) {\r
984     sounds[(int)SoundMove].name = strdup("");\r
985     appData.ringBellAfterMoves = TRUE;\r
986   }\r
987   if (appData.debugMode) {\r
988     debugFP = fopen(appData.nameOfDebugFile, "w");\r
989     setbuf(debugFP, NULL);\r
990   }\r
991 \r
992   LoadLanguageFile(appData.language);\r
993 \r
994   InitBackEnd1();\r
995 \r
996 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
997 //  InitEngineUCI( installDir, &second );\r
998 \r
999   /* Create a main window for this application instance. */\r
1000   hwnd = CreateWindow(szAppName, szTitle,\r
1001                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1002                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1003                       NULL, NULL, hInstance, NULL);\r
1004   hwndMain = hwnd;\r
1005 \r
1006   /* If window could not be created, return "failure" */\r
1007   if (!hwnd) {\r
1008     return (FALSE);\r
1009   }\r
1010 \r
1011   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1012   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
1013       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1014 \r
1015       if (first.programLogo == NULL && appData.debugMode) {\r
1016           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
1017       }\r
1018   } else if(appData.autoLogo) {\r
1019       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
1020         char buf[MSG_SIZ];\r
1021           snprintf(buf, MSG_SIZ, "%s/logo.bmp", appData.firstDirectory);\r
1022         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
1023       }\r
1024   }\r
1025 \r
1026   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
1027       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1028 \r
1029       if (second.programLogo == NULL && appData.debugMode) {\r
1030           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
1031       }\r
1032   } else if(appData.autoLogo) {\r
1033       char buf[MSG_SIZ];\r
1034       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
1035         snprintf(buf, MSG_SIZ, "logos\\%s.bmp", appData.icsHost);\r
1036         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1037       } else\r
1038       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
1039         snprintf(buf, MSG_SIZ, "%s\\logo.bmp", appData.secondDirectory);\r
1040         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
1041       }\r
1042   }\r
1043 \r
1044   SetUserLogo();\r
1045 \r
1046   iconWhite = LoadIcon(hInstance, "icon_white");\r
1047   iconBlack = LoadIcon(hInstance, "icon_black");\r
1048   iconCurrent = iconWhite;\r
1049   InitDrawingColors();\r
1050   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1051   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1052   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1053     /* Compute window size for each board size, and use the largest\r
1054        size that fits on this screen as the default. */\r
1055     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1056     if (boardSize == (BoardSize)-1 &&\r
1057         winH <= screenHeight\r
1058            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1059         && winW <= screenWidth) {\r
1060       boardSize = (BoardSize)ibs;\r
1061     }\r
1062   }\r
1063 \r
1064   InitDrawingSizes(boardSize, 0);\r
1065   InitMenuChecks();\r
1066   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1067 \r
1068   /* [AS] Load textures if specified */\r
1069   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1070   \r
1071   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1072       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1073       liteBackTextureMode = appData.liteBackTextureMode;\r
1074 \r
1075       if (liteBackTexture == NULL && appData.debugMode) {\r
1076           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1077       }\r
1078   }\r
1079   \r
1080   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1081       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1082       darkBackTextureMode = appData.darkBackTextureMode;\r
1083 \r
1084       if (darkBackTexture == NULL && appData.debugMode) {\r
1085           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1086       }\r
1087   }\r
1088 \r
1089   mysrandom( (unsigned) time(NULL) );\r
1090 \r
1091   /* [AS] Restore layout */\r
1092   if( wpMoveHistory.visible ) {\r
1093       MoveHistoryPopUp();\r
1094   }\r
1095 \r
1096   if( wpEvalGraph.visible ) {\r
1097       EvalGraphPopUp();\r
1098   }\r
1099 \r
1100   if( wpEngineOutput.visible ) {\r
1101       EngineOutputPopUp();\r
1102   }\r
1103 \r
1104   /* Make the window visible; update its client area; and return "success" */\r
1105   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1106   wp.length = sizeof(WINDOWPLACEMENT);\r
1107   wp.flags = 0;\r
1108   wp.showCmd = nCmdShow;\r
1109   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1110   wp.rcNormalPosition.left = wpMain.x;\r
1111   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1112   wp.rcNormalPosition.top = wpMain.y;\r
1113   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1114   SetWindowPlacement(hwndMain, &wp);\r
1115 \r
1116   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1117 \r
1118   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1119                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1120 \r
1121   if (hwndConsole) {\r
1122 #if AOT_CONSOLE\r
1123     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1124                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1125 #endif\r
1126     ShowWindow(hwndConsole, nCmdShow);\r
1127     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
1128       char buf[MSG_SIZ], *p = buf, *q;\r
1129         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
1130       do {\r
1131         q = strchr(p, ';');\r
1132         if(q) *q++ = 0;\r
1133         if(*p) ChatPopUp(p);\r
1134       } while(p=q);\r
1135     }\r
1136     SetActiveWindow(hwndConsole);\r
1137   }\r
1138   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1139   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1140 \r
1141   return TRUE;\r
1142 \r
1143 }\r
1144 \r
1145 VOID\r
1146 InitMenuChecks()\r
1147 {\r
1148   HMENU hmenu = GetMenu(hwndMain);\r
1149 \r
1150   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1151                         MF_BYCOMMAND|((appData.icsActive &&\r
1152                                        *appData.icsCommPort != NULLCHAR) ?\r
1153                                       MF_ENABLED : MF_GRAYED));\r
1154   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1155                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1156                                      MF_CHECKED : MF_UNCHECKED));\r
1157 }\r
1158 \r
1159 //---------------------------------------------------------------------------------------------------------\r
1160 \r
1161 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1162 #define XBOARD FALSE\r
1163 \r
1164 #define OPTCHAR "/"\r
1165 #define SEPCHAR "="\r
1166 \r
1167 #include "args.h"\r
1168 \r
1169 // front-end part of option handling\r
1170 \r
1171 VOID\r
1172 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1173 {\r
1174   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1175   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1176   DeleteDC(hdc);\r
1177   lf->lfWidth = 0;\r
1178   lf->lfEscapement = 0;\r
1179   lf->lfOrientation = 0;\r
1180   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1181   lf->lfItalic = mfp->italic;\r
1182   lf->lfUnderline = mfp->underline;\r
1183   lf->lfStrikeOut = mfp->strikeout;\r
1184   lf->lfCharSet = mfp->charset;\r
1185   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1186   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1187   lf->lfQuality = DEFAULT_QUALITY;\r
1188   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1189     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1190 }\r
1191 \r
1192 void\r
1193 CreateFontInMF(MyFont *mf)\r
1194\r
1195   LFfromMFP(&mf->lf, &mf->mfp);\r
1196   if (mf->hf) DeleteObject(mf->hf);\r
1197   mf->hf = CreateFontIndirect(&mf->lf);\r
1198 }\r
1199 \r
1200 // [HGM] This platform-dependent table provides the location for storing the color info\r
1201 void *\r
1202 colorVariable[] = {\r
1203   &whitePieceColor, \r
1204   &blackPieceColor, \r
1205   &lightSquareColor,\r
1206   &darkSquareColor, \r
1207   &highlightSquareColor,\r
1208   &premoveHighlightColor,\r
1209   NULL,\r
1210   &consoleBackgroundColor,\r
1211   &appData.fontForeColorWhite,\r
1212   &appData.fontBackColorWhite,\r
1213   &appData.fontForeColorBlack,\r
1214   &appData.fontBackColorBlack,\r
1215   &appData.evalHistColorWhite,\r
1216   &appData.evalHistColorBlack,\r
1217   &appData.highlightArrowColor,\r
1218 };\r
1219 \r
1220 /* Command line font name parser.  NULL name means do nothing.\r
1221    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1222    For backward compatibility, syntax without the colon is also\r
1223    accepted, but font names with digits in them won't work in that case.\r
1224 */\r
1225 VOID\r
1226 ParseFontName(char *name, MyFontParams *mfp)\r
1227 {\r
1228   char *p, *q;\r
1229   if (name == NULL) return;\r
1230   p = name;\r
1231   q = strchr(p, ':');\r
1232   if (q) {\r
1233     if (q - p >= sizeof(mfp->faceName))\r
1234       ExitArgError(_("Font name too long:"), name);\r
1235     memcpy(mfp->faceName, p, q - p);\r
1236     mfp->faceName[q - p] = NULLCHAR;\r
1237     p = q + 1;\r
1238   } else {\r
1239     q = mfp->faceName;\r
1240     while (*p && !isdigit(*p)) {\r
1241       *q++ = *p++;\r
1242       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1243         ExitArgError(_("Font name too long:"), name);\r
1244     }\r
1245     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1246     *q = NULLCHAR;\r
1247   }\r
1248   if (!*p) ExitArgError(_("Font point size missing:"), name);\r
1249   mfp->pointSize = (float) atof(p);\r
1250   mfp->bold = (strchr(p, 'b') != NULL);\r
1251   mfp->italic = (strchr(p, 'i') != NULL);\r
1252   mfp->underline = (strchr(p, 'u') != NULL);\r
1253   mfp->strikeout = (strchr(p, 's') != NULL);\r
1254   mfp->charset = DEFAULT_CHARSET;\r
1255   q = strchr(p, 'c');\r
1256   if (q)\r
1257     mfp->charset = (BYTE) atoi(q+1);\r
1258 }\r
1259 \r
1260 void\r
1261 ParseFont(char *name, int number)\r
1262 { // wrapper to shield back-end from 'font'\r
1263   ParseFontName(name, &font[boardSize][number]->mfp);\r
1264 }\r
1265 \r
1266 void\r
1267 SetFontDefaults()\r
1268 { // in WB  we have a 2D array of fonts; this initializes their description\r
1269   int i, j;\r
1270   /* Point font array elements to structures and\r
1271      parse default font names */\r
1272   for (i=0; i<NUM_FONTS; i++) {\r
1273     for (j=0; j<NUM_SIZES; j++) {\r
1274       font[j][i] = &fontRec[j][i];\r
1275       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1276     }\r
1277   }\r
1278 }\r
1279 \r
1280 void\r
1281 CreateFonts()\r
1282 { // here we create the actual fonts from the selected descriptions\r
1283   int i, j;\r
1284   for (i=0; i<NUM_FONTS; i++) {\r
1285     for (j=0; j<NUM_SIZES; j++) {\r
1286       CreateFontInMF(font[j][i]);\r
1287     }\r
1288   }\r
1289 }\r
1290 /* Color name parser.\r
1291    X version accepts X color names, but this one\r
1292    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1293 COLORREF\r
1294 ParseColorName(char *name)\r
1295 {\r
1296   int red, green, blue, count;\r
1297   char buf[MSG_SIZ];\r
1298 \r
1299   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1300   if (count != 3) {\r
1301     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1302       &red, &green, &blue);\r
1303   }\r
1304   if (count != 3) {\r
1305     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1306     DisplayError(buf, 0);\r
1307     return RGB(0, 0, 0);\r
1308   }\r
1309   return PALETTERGB(red, green, blue);\r
1310 }\r
1311 \r
1312 void\r
1313 ParseColor(int n, char *name)\r
1314 { // for WinBoard the color is an int, which needs to be derived from the string\r
1315   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1316 }\r
1317 \r
1318 void\r
1319 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1320 {\r
1321   char *e = argValue;\r
1322   int eff = 0;\r
1323 \r
1324   while (*e) {\r
1325     if (*e == 'b')      eff |= CFE_BOLD;\r
1326     else if (*e == 'i') eff |= CFE_ITALIC;\r
1327     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1328     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1329     else if (*e == '#' || isdigit(*e)) break;\r
1330     e++;\r
1331   }\r
1332   *effects = eff;\r
1333   *color   = ParseColorName(e);\r
1334 }\r
1335 \r
1336 void\r
1337 ParseTextAttribs(ColorClass cc, char *s)\r
1338 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1339     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1340     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1341 }\r
1342 \r
1343 void\r
1344 ParseBoardSize(void *addr, char *name)\r
1345 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1346   BoardSize bs = SizeTiny;\r
1347   while (sizeInfo[bs].name != NULL) {\r
1348     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1349         *(BoardSize *)addr = bs;\r
1350         return;\r
1351     }\r
1352     bs++;\r
1353   }\r
1354   ExitArgError(_("Unrecognized board size value"), name);\r
1355 }\r
1356 \r
1357 void\r
1358 LoadAllSounds()\r
1359 { // [HGM] import name from appData first\r
1360   ColorClass cc;\r
1361   SoundClass sc;\r
1362   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1363     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1364     textAttribs[cc].sound.data = NULL;\r
1365     MyLoadSound(&textAttribs[cc].sound);\r
1366   }\r
1367   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1368     textAttribs[cc].sound.name = strdup("");\r
1369     textAttribs[cc].sound.data = NULL;\r
1370   }\r
1371   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1372     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1373     sounds[sc].data = NULL;\r
1374     MyLoadSound(&sounds[sc]);\r
1375   }\r
1376 }\r
1377 \r
1378 void\r
1379 SetCommPortDefaults()\r
1380 {\r
1381    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1382   dcb.DCBlength = sizeof(DCB);\r
1383   dcb.BaudRate = 9600;\r
1384   dcb.fBinary = TRUE;\r
1385   dcb.fParity = FALSE;\r
1386   dcb.fOutxCtsFlow = FALSE;\r
1387   dcb.fOutxDsrFlow = FALSE;\r
1388   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1389   dcb.fDsrSensitivity = FALSE;\r
1390   dcb.fTXContinueOnXoff = TRUE;\r
1391   dcb.fOutX = FALSE;\r
1392   dcb.fInX = FALSE;\r
1393   dcb.fNull = FALSE;\r
1394   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1395   dcb.fAbortOnError = FALSE;\r
1396   dcb.ByteSize = 7;\r
1397   dcb.Parity = SPACEPARITY;\r
1398   dcb.StopBits = ONESTOPBIT;\r
1399 }\r
1400 \r
1401 // [HGM] args: these three cases taken out to stay in front-end\r
1402 void\r
1403 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1404 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1405         // while the curent board size determines the element. This system should be ported to XBoard.\r
1406         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1407         int bs;\r
1408         for (bs=0; bs<NUM_SIZES; bs++) {\r
1409           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1410           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1411           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1412             ad->argName, mfp->faceName, mfp->pointSize,\r
1413             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1414             mfp->bold ? "b" : "",\r
1415             mfp->italic ? "i" : "",\r
1416             mfp->underline ? "u" : "",\r
1417             mfp->strikeout ? "s" : "",\r
1418             (int)mfp->charset);\r
1419         }\r
1420       }\r
1421 \r
1422 void\r
1423 ExportSounds()\r
1424 { // [HGM] copy the names from the internal WB variables to appData\r
1425   ColorClass cc;\r
1426   SoundClass sc;\r
1427   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1428     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1429   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1430     (&appData.soundMove)[sc] = sounds[sc].name;\r
1431 }\r
1432 \r
1433 void\r
1434 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1435 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1436         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1437         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1438           (ta->effects & CFE_BOLD) ? "b" : "",\r
1439           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1440           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1441           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1442           (ta->effects) ? " " : "",\r
1443           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1444       }\r
1445 \r
1446 void\r
1447 SaveColor(FILE *f, ArgDescriptor *ad)\r
1448 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1449         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1450         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1451           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1452 }\r
1453 \r
1454 void\r
1455 SaveBoardSize(FILE *f, char *name, void *addr)\r
1456 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1457   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1458 }\r
1459 \r
1460 void\r
1461 ParseCommPortSettings(char *s)\r
1462 { // wrapper to keep dcb from back-end\r
1463   ParseCommSettings(s, &dcb);\r
1464 }\r
1465 \r
1466 void\r
1467 GetWindowCoords()\r
1468 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1469   GetActualPlacement(hwndMain, &wpMain);\r
1470   GetActualPlacement(hwndConsole, &wpConsole);\r
1471   GetActualPlacement(commentDialog, &wpComment);\r
1472   GetActualPlacement(editTagsDialog, &wpTags);\r
1473   GetActualPlacement(gameListDialog, &wpGameList);\r
1474   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1475   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1476   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1477 }\r
1478 \r
1479 void\r
1480 PrintCommPortSettings(FILE *f, char *name)\r
1481 { // wrapper to shield back-end from DCB\r
1482       PrintCommSettings(f, name, &dcb);\r
1483 }\r
1484 \r
1485 int\r
1486 MySearchPath(char *installDir, char *name, char *fullname)\r
1487 {\r
1488   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1489   if(name[0]== '%') {\r
1490     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1491     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1492       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1493       *strchr(buf, '%') = 0;\r
1494       strcat(fullname, getenv(buf));\r
1495       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1496     }\r
1497     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1498     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1499     return (int) strlen(fullname);\r
1500   }\r
1501   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1502 }\r
1503 \r
1504 int\r
1505 MyGetFullPathName(char *name, char *fullname)\r
1506 {\r
1507   char *dummy;\r
1508   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1509 }\r
1510 \r
1511 int\r
1512 MainWindowUp()\r
1513 { // [HGM] args: allows testing if main window is realized from back-end\r
1514   return hwndMain != NULL;\r
1515 }\r
1516 \r
1517 void\r
1518 PopUpStartupDialog()\r
1519 {\r
1520     FARPROC lpProc;\r
1521     \r
1522     LoadLanguageFile(appData.language);\r
1523     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1524     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1525     FreeProcInstance(lpProc);\r
1526 }\r
1527 \r
1528 /*---------------------------------------------------------------------------*\\r
1529  *\r
1530  * GDI board drawing routines\r
1531  *\r
1532 \*---------------------------------------------------------------------------*/\r
1533 \r
1534 /* [AS] Draw square using background texture */\r
1535 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1536 {\r
1537     XFORM   x;\r
1538 \r
1539     if( mode == 0 ) {\r
1540         return; /* Should never happen! */\r
1541     }\r
1542 \r
1543     SetGraphicsMode( dst, GM_ADVANCED );\r
1544 \r
1545     switch( mode ) {\r
1546     case 1:\r
1547         /* Identity */\r
1548         break;\r
1549     case 2:\r
1550         /* X reflection */\r
1551         x.eM11 = -1.0;\r
1552         x.eM12 = 0;\r
1553         x.eM21 = 0;\r
1554         x.eM22 = 1.0;\r
1555         x.eDx = (FLOAT) dw + dx - 1;\r
1556         x.eDy = 0;\r
1557         dx = 0;\r
1558         SetWorldTransform( dst, &x );\r
1559         break;\r
1560     case 3:\r
1561         /* Y reflection */\r
1562         x.eM11 = 1.0;\r
1563         x.eM12 = 0;\r
1564         x.eM21 = 0;\r
1565         x.eM22 = -1.0;\r
1566         x.eDx = 0;\r
1567         x.eDy = (FLOAT) dh + dy - 1;\r
1568         dy = 0;\r
1569         SetWorldTransform( dst, &x );\r
1570         break;\r
1571     case 4:\r
1572         /* X/Y flip */\r
1573         x.eM11 = 0;\r
1574         x.eM12 = 1.0;\r
1575         x.eM21 = 1.0;\r
1576         x.eM22 = 0;\r
1577         x.eDx = (FLOAT) dx;\r
1578         x.eDy = (FLOAT) dy;\r
1579         dx = 0;\r
1580         dy = 0;\r
1581         SetWorldTransform( dst, &x );\r
1582         break;\r
1583     }\r
1584 \r
1585     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1586 \r
1587     x.eM11 = 1.0;\r
1588     x.eM12 = 0;\r
1589     x.eM21 = 0;\r
1590     x.eM22 = 1.0;\r
1591     x.eDx = 0;\r
1592     x.eDy = 0;\r
1593     SetWorldTransform( dst, &x );\r
1594 \r
1595     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1596 }\r
1597 \r
1598 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1599 enum {\r
1600     PM_WP = (int) WhitePawn, \r
1601     PM_WN = (int) WhiteKnight, \r
1602     PM_WB = (int) WhiteBishop, \r
1603     PM_WR = (int) WhiteRook, \r
1604     PM_WQ = (int) WhiteQueen, \r
1605     PM_WF = (int) WhiteFerz, \r
1606     PM_WW = (int) WhiteWazir, \r
1607     PM_WE = (int) WhiteAlfil, \r
1608     PM_WM = (int) WhiteMan, \r
1609     PM_WO = (int) WhiteCannon, \r
1610     PM_WU = (int) WhiteUnicorn, \r
1611     PM_WH = (int) WhiteNightrider, \r
1612     PM_WA = (int) WhiteAngel, \r
1613     PM_WC = (int) WhiteMarshall, \r
1614     PM_WAB = (int) WhiteCardinal, \r
1615     PM_WD = (int) WhiteDragon, \r
1616     PM_WL = (int) WhiteLance, \r
1617     PM_WS = (int) WhiteCobra, \r
1618     PM_WV = (int) WhiteFalcon, \r
1619     PM_WSG = (int) WhiteSilver, \r
1620     PM_WG = (int) WhiteGrasshopper, \r
1621     PM_WK = (int) WhiteKing,\r
1622     PM_BP = (int) BlackPawn, \r
1623     PM_BN = (int) BlackKnight, \r
1624     PM_BB = (int) BlackBishop, \r
1625     PM_BR = (int) BlackRook, \r
1626     PM_BQ = (int) BlackQueen, \r
1627     PM_BF = (int) BlackFerz, \r
1628     PM_BW = (int) BlackWazir, \r
1629     PM_BE = (int) BlackAlfil, \r
1630     PM_BM = (int) BlackMan,\r
1631     PM_BO = (int) BlackCannon, \r
1632     PM_BU = (int) BlackUnicorn, \r
1633     PM_BH = (int) BlackNightrider, \r
1634     PM_BA = (int) BlackAngel, \r
1635     PM_BC = (int) BlackMarshall, \r
1636     PM_BG = (int) BlackGrasshopper, \r
1637     PM_BAB = (int) BlackCardinal,\r
1638     PM_BD = (int) BlackDragon,\r
1639     PM_BL = (int) BlackLance,\r
1640     PM_BS = (int) BlackCobra,\r
1641     PM_BV = (int) BlackFalcon,\r
1642     PM_BSG = (int) BlackSilver,\r
1643     PM_BK = (int) BlackKing\r
1644 };\r
1645 \r
1646 static HFONT hPieceFont = NULL;\r
1647 static HBITMAP hPieceMask[(int) EmptySquare];\r
1648 static HBITMAP hPieceFace[(int) EmptySquare];\r
1649 static int fontBitmapSquareSize = 0;\r
1650 static char pieceToFontChar[(int) EmptySquare] =\r
1651                               { 'p', 'n', 'b', 'r', 'q', \r
1652                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1653                       'k', 'o', 'm', 'v', 't', 'w', \r
1654                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1655                                                               'l' };\r
1656 \r
1657 extern BOOL SetCharTable( char *table, const char * map );\r
1658 /* [HGM] moved to backend.c */\r
1659 \r
1660 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1661 {\r
1662     HBRUSH hbrush;\r
1663     BYTE r1 = GetRValue( color );\r
1664     BYTE g1 = GetGValue( color );\r
1665     BYTE b1 = GetBValue( color );\r
1666     BYTE r2 = r1 / 2;\r
1667     BYTE g2 = g1 / 2;\r
1668     BYTE b2 = b1 / 2;\r
1669     RECT rc;\r
1670 \r
1671     /* Create a uniform background first */\r
1672     hbrush = CreateSolidBrush( color );\r
1673     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1674     FillRect( hdc, &rc, hbrush );\r
1675     DeleteObject( hbrush );\r
1676     \r
1677     if( mode == 1 ) {\r
1678         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1679         int steps = squareSize / 2;\r
1680         int i;\r
1681 \r
1682         for( i=0; i<steps; i++ ) {\r
1683             BYTE r = r1 - (r1-r2) * i / steps;\r
1684             BYTE g = g1 - (g1-g2) * i / steps;\r
1685             BYTE b = b1 - (b1-b2) * i / steps;\r
1686 \r
1687             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1688             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1689             FillRect( hdc, &rc, hbrush );\r
1690             DeleteObject(hbrush);\r
1691         }\r
1692     }\r
1693     else if( mode == 2 ) {\r
1694         /* Diagonal gradient, good more or less for every piece */\r
1695         POINT triangle[3];\r
1696         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1697         HBRUSH hbrush_old;\r
1698         int steps = squareSize;\r
1699         int i;\r
1700 \r
1701         triangle[0].x = squareSize - steps;\r
1702         triangle[0].y = squareSize;\r
1703         triangle[1].x = squareSize;\r
1704         triangle[1].y = squareSize;\r
1705         triangle[2].x = squareSize;\r
1706         triangle[2].y = squareSize - steps;\r
1707 \r
1708         for( i=0; i<steps; i++ ) {\r
1709             BYTE r = r1 - (r1-r2) * i / steps;\r
1710             BYTE g = g1 - (g1-g2) * i / steps;\r
1711             BYTE b = b1 - (b1-b2) * i / steps;\r
1712 \r
1713             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1714             hbrush_old = SelectObject( hdc, hbrush );\r
1715             Polygon( hdc, triangle, 3 );\r
1716             SelectObject( hdc, hbrush_old );\r
1717             DeleteObject(hbrush);\r
1718             triangle[0].x++;\r
1719             triangle[2].y++;\r
1720         }\r
1721 \r
1722         SelectObject( hdc, hpen );\r
1723     }\r
1724 }\r
1725 \r
1726 /*\r
1727     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1728     seems to work ok. The main problem here is to find the "inside" of a chess\r
1729     piece: follow the steps as explained below.\r
1730 */\r
1731 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1732 {\r
1733     HBITMAP hbm;\r
1734     HBITMAP hbm_old;\r
1735     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1736     RECT rc;\r
1737     SIZE sz;\r
1738     POINT pt;\r
1739     int backColor = whitePieceColor; \r
1740     int foreColor = blackPieceColor;\r
1741     \r
1742     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1743         backColor = appData.fontBackColorWhite;\r
1744         foreColor = appData.fontForeColorWhite;\r
1745     }\r
1746     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1747         backColor = appData.fontBackColorBlack;\r
1748         foreColor = appData.fontForeColorBlack;\r
1749     }\r
1750 \r
1751     /* Mask */\r
1752     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1753 \r
1754     hbm_old = SelectObject( hdc, hbm );\r
1755 \r
1756     rc.left = 0;\r
1757     rc.top = 0;\r
1758     rc.right = squareSize;\r
1759     rc.bottom = squareSize;\r
1760 \r
1761     /* Step 1: background is now black */\r
1762     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1763 \r
1764     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1765 \r
1766     pt.x = (squareSize - sz.cx) / 2;\r
1767     pt.y = (squareSize - sz.cy) / 2;\r
1768 \r
1769     SetBkMode( hdc, TRANSPARENT );\r
1770     SetTextColor( hdc, chroma );\r
1771     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1772     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1773 \r
1774     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1775     /* Step 3: the area outside the piece is filled with white */\r
1776 //    FloodFill( hdc, 0, 0, chroma );\r
1777     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1778     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1779     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1780     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1781     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1782     /* \r
1783         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1784         but if the start point is not inside the piece we're lost!\r
1785         There should be a better way to do this... if we could create a region or path\r
1786         from the fill operation we would be fine for example.\r
1787     */\r
1788 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1789     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1790 \r
1791     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1792         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1793         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1794 \r
1795         SelectObject( dc2, bm2 );\r
1796         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1797         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1798         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1799         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1800         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1801 \r
1802         DeleteDC( dc2 );\r
1803         DeleteObject( bm2 );\r
1804     }\r
1805 \r
1806     SetTextColor( hdc, 0 );\r
1807     /* \r
1808         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1809         draw the piece again in black for safety.\r
1810     */\r
1811     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1812 \r
1813     SelectObject( hdc, hbm_old );\r
1814 \r
1815     if( hPieceMask[index] != NULL ) {\r
1816         DeleteObject( hPieceMask[index] );\r
1817     }\r
1818 \r
1819     hPieceMask[index] = hbm;\r
1820 \r
1821     /* Face */\r
1822     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1823 \r
1824     SelectObject( hdc, hbm );\r
1825 \r
1826     {\r
1827         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1828         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1829         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1830 \r
1831         SelectObject( dc1, hPieceMask[index] );\r
1832         SelectObject( dc2, bm2 );\r
1833         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1834         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1835         \r
1836         /* \r
1837             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1838             the piece background and deletes (makes transparent) the rest.\r
1839             Thanks to that mask, we are free to paint the background with the greates\r
1840             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1841             We use this, to make gradients and give the pieces a "roundish" look.\r
1842         */\r
1843         SetPieceBackground( hdc, backColor, 2 );\r
1844         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1845 \r
1846         DeleteDC( dc2 );\r
1847         DeleteDC( dc1 );\r
1848         DeleteObject( bm2 );\r
1849     }\r
1850 \r
1851     SetTextColor( hdc, foreColor );\r
1852     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1853 \r
1854     SelectObject( hdc, hbm_old );\r
1855 \r
1856     if( hPieceFace[index] != NULL ) {\r
1857         DeleteObject( hPieceFace[index] );\r
1858     }\r
1859 \r
1860     hPieceFace[index] = hbm;\r
1861 }\r
1862 \r
1863 static int TranslatePieceToFontPiece( int piece )\r
1864 {\r
1865     switch( piece ) {\r
1866     case BlackPawn:\r
1867         return PM_BP;\r
1868     case BlackKnight:\r
1869         return PM_BN;\r
1870     case BlackBishop:\r
1871         return PM_BB;\r
1872     case BlackRook:\r
1873         return PM_BR;\r
1874     case BlackQueen:\r
1875         return PM_BQ;\r
1876     case BlackKing:\r
1877         return PM_BK;\r
1878     case WhitePawn:\r
1879         return PM_WP;\r
1880     case WhiteKnight:\r
1881         return PM_WN;\r
1882     case WhiteBishop:\r
1883         return PM_WB;\r
1884     case WhiteRook:\r
1885         return PM_WR;\r
1886     case WhiteQueen:\r
1887         return PM_WQ;\r
1888     case WhiteKing:\r
1889         return PM_WK;\r
1890 \r
1891     case BlackAngel:\r
1892         return PM_BA;\r
1893     case BlackMarshall:\r
1894         return PM_BC;\r
1895     case BlackFerz:\r
1896         return PM_BF;\r
1897     case BlackNightrider:\r
1898         return PM_BH;\r
1899     case BlackAlfil:\r
1900         return PM_BE;\r
1901     case BlackWazir:\r
1902         return PM_BW;\r
1903     case BlackUnicorn:\r
1904         return PM_BU;\r
1905     case BlackCannon:\r
1906         return PM_BO;\r
1907     case BlackGrasshopper:\r
1908         return PM_BG;\r
1909     case BlackMan:\r
1910         return PM_BM;\r
1911     case BlackSilver:\r
1912         return PM_BSG;\r
1913     case BlackLance:\r
1914         return PM_BL;\r
1915     case BlackFalcon:\r
1916         return PM_BV;\r
1917     case BlackCobra:\r
1918         return PM_BS;\r
1919     case BlackCardinal:\r
1920         return PM_BAB;\r
1921     case BlackDragon:\r
1922         return PM_BD;\r
1923 \r
1924     case WhiteAngel:\r
1925         return PM_WA;\r
1926     case WhiteMarshall:\r
1927         return PM_WC;\r
1928     case WhiteFerz:\r
1929         return PM_WF;\r
1930     case WhiteNightrider:\r
1931         return PM_WH;\r
1932     case WhiteAlfil:\r
1933         return PM_WE;\r
1934     case WhiteWazir:\r
1935         return PM_WW;\r
1936     case WhiteUnicorn:\r
1937         return PM_WU;\r
1938     case WhiteCannon:\r
1939         return PM_WO;\r
1940     case WhiteGrasshopper:\r
1941         return PM_WG;\r
1942     case WhiteMan:\r
1943         return PM_WM;\r
1944     case WhiteSilver:\r
1945         return PM_WSG;\r
1946     case WhiteLance:\r
1947         return PM_WL;\r
1948     case WhiteFalcon:\r
1949         return PM_WV;\r
1950     case WhiteCobra:\r
1951         return PM_WS;\r
1952     case WhiteCardinal:\r
1953         return PM_WAB;\r
1954     case WhiteDragon:\r
1955         return PM_WD;\r
1956     }\r
1957 \r
1958     return 0;\r
1959 }\r
1960 \r
1961 void CreatePiecesFromFont()\r
1962 {\r
1963     LOGFONT lf;\r
1964     HDC hdc_window = NULL;\r
1965     HDC hdc = NULL;\r
1966     HFONT hfont_old;\r
1967     int fontHeight;\r
1968     int i;\r
1969 \r
1970     if( fontBitmapSquareSize < 0 ) {\r
1971         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
1972         return;\r
1973     }\r
1974 \r
1975     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
1976         fontBitmapSquareSize = -1;\r
1977         return;\r
1978     }\r
1979 \r
1980     if( fontBitmapSquareSize != squareSize ) {\r
1981         hdc_window = GetDC( hwndMain );\r
1982         hdc = CreateCompatibleDC( hdc_window );\r
1983 \r
1984         if( hPieceFont != NULL ) {\r
1985             DeleteObject( hPieceFont );\r
1986         }\r
1987         else {\r
1988             for( i=0; i<=(int)BlackKing; i++ ) {\r
1989                 hPieceMask[i] = NULL;\r
1990                 hPieceFace[i] = NULL;\r
1991             }\r
1992         }\r
1993 \r
1994         fontHeight = 75;\r
1995 \r
1996         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
1997             fontHeight = appData.fontPieceSize;\r
1998         }\r
1999 \r
2000         fontHeight = (fontHeight * squareSize) / 100;\r
2001 \r
2002         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2003         lf.lfWidth = 0;\r
2004         lf.lfEscapement = 0;\r
2005         lf.lfOrientation = 0;\r
2006         lf.lfWeight = FW_NORMAL;\r
2007         lf.lfItalic = 0;\r
2008         lf.lfUnderline = 0;\r
2009         lf.lfStrikeOut = 0;\r
2010         lf.lfCharSet = DEFAULT_CHARSET;\r
2011         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2012         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2013         lf.lfQuality = PROOF_QUALITY;\r
2014         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2015         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2016         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2017 \r
2018         hPieceFont = CreateFontIndirect( &lf );\r
2019 \r
2020         if( hPieceFont == NULL ) {\r
2021             fontBitmapSquareSize = -2;\r
2022         }\r
2023         else {\r
2024             /* Setup font-to-piece character table */\r
2025             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2026                 /* No (or wrong) global settings, try to detect the font */\r
2027                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2028                     /* Alpha */\r
2029                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2030                 }\r
2031                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2032                     /* DiagramTT* family */\r
2033                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2034                 }\r
2035                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2036                     /* Fairy symbols */\r
2037                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2038                 }\r
2039                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2040                     /* Good Companion (Some characters get warped as literal :-( */\r
2041                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2042                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2043                     SetCharTable(pieceToFontChar, s);\r
2044                 }\r
2045                 else {\r
2046                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2047                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2048                 }\r
2049             }\r
2050 \r
2051             /* Create bitmaps */\r
2052             hfont_old = SelectObject( hdc, hPieceFont );\r
2053             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2054                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2055                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2056 \r
2057             SelectObject( hdc, hfont_old );\r
2058 \r
2059             fontBitmapSquareSize = squareSize;\r
2060         }\r
2061     }\r
2062 \r
2063     if( hdc != NULL ) {\r
2064         DeleteDC( hdc );\r
2065     }\r
2066 \r
2067     if( hdc_window != NULL ) {\r
2068         ReleaseDC( hwndMain, hdc_window );\r
2069     }\r
2070 }\r
2071 \r
2072 HBITMAP\r
2073 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2074 {\r
2075   char name[128];\r
2076 \r
2077     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2078   if (gameInfo.event &&\r
2079       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2080       strcmp(name, "k80s") == 0) {\r
2081     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2082   }\r
2083   return LoadBitmap(hinst, name);\r
2084 }\r
2085 \r
2086 \r
2087 /* Insert a color into the program's logical palette\r
2088    structure.  This code assumes the given color is\r
2089    the result of the RGB or PALETTERGB macro, and it\r
2090    knows how those macros work (which is documented).\r
2091 */\r
2092 VOID\r
2093 InsertInPalette(COLORREF color)\r
2094 {\r
2095   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2096 \r
2097   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2098     DisplayFatalError(_("Too many colors"), 0, 1);\r
2099     pLogPal->palNumEntries--;\r
2100     return;\r
2101   }\r
2102 \r
2103   pe->peFlags = (char) 0;\r
2104   pe->peRed = (char) (0xFF & color);\r
2105   pe->peGreen = (char) (0xFF & (color >> 8));\r
2106   pe->peBlue = (char) (0xFF & (color >> 16));\r
2107   return;\r
2108 }\r
2109 \r
2110 \r
2111 VOID\r
2112 InitDrawingColors()\r
2113 {\r
2114   if (pLogPal == NULL) {\r
2115     /* Allocate enough memory for a logical palette with\r
2116      * PALETTESIZE entries and set the size and version fields\r
2117      * of the logical palette structure.\r
2118      */\r
2119     pLogPal = (NPLOGPALETTE)\r
2120       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2121                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2122     pLogPal->palVersion    = 0x300;\r
2123   }\r
2124   pLogPal->palNumEntries = 0;\r
2125 \r
2126   InsertInPalette(lightSquareColor);\r
2127   InsertInPalette(darkSquareColor);\r
2128   InsertInPalette(whitePieceColor);\r
2129   InsertInPalette(blackPieceColor);\r
2130   InsertInPalette(highlightSquareColor);\r
2131   InsertInPalette(premoveHighlightColor);\r
2132 \r
2133   /*  create a logical color palette according the information\r
2134    *  in the LOGPALETTE structure.\r
2135    */\r
2136   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2137 \r
2138   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2139   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2140   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2141   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2142   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2143   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2144   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2145   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2146   /* [AS] Force rendering of the font-based pieces */\r
2147   if( fontBitmapSquareSize > 0 ) {\r
2148     fontBitmapSquareSize = 0;\r
2149   }\r
2150 }\r
2151 \r
2152 \r
2153 int\r
2154 BoardWidth(int boardSize, int n)\r
2155 { /* [HGM] argument n added to allow different width and height */\r
2156   int lineGap = sizeInfo[boardSize].lineGap;\r
2157 \r
2158   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2159       lineGap = appData.overrideLineGap;\r
2160   }\r
2161 \r
2162   return (n + 1) * lineGap +\r
2163           n * sizeInfo[boardSize].squareSize;\r
2164 }\r
2165 \r
2166 /* Respond to board resize by dragging edge */\r
2167 VOID\r
2168 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2169 {\r
2170   BoardSize newSize = NUM_SIZES - 1;\r
2171   static int recurse = 0;\r
2172   if (IsIconic(hwndMain)) return;\r
2173   if (recurse > 0) return;\r
2174   recurse++;\r
2175   while (newSize > 0) {\r
2176         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2177         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2178            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2179     newSize--;\r
2180   } \r
2181   boardSize = newSize;\r
2182   InitDrawingSizes(boardSize, flags);\r
2183   recurse--;\r
2184 }\r
2185 \r
2186 \r
2187 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2188 \r
2189 VOID\r
2190 InitDrawingSizes(BoardSize boardSize, int flags)\r
2191 {\r
2192   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2193   ChessSquare piece;\r
2194   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2195   HDC hdc;\r
2196   SIZE clockSize, messageSize;\r
2197   HFONT oldFont;\r
2198   char buf[MSG_SIZ];\r
2199   char *str;\r
2200   HMENU hmenu = GetMenu(hwndMain);\r
2201   RECT crect, wrect, oldRect;\r
2202   int offby;\r
2203   LOGBRUSH logbrush;\r
2204 \r
2205   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2206   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2207 \r
2208   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2209   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2210 \r
2211   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2212   oldRect.top = wpMain.y;\r
2213   oldRect.right = wpMain.x + wpMain.width;\r
2214   oldRect.bottom = wpMain.y + wpMain.height;\r
2215 \r
2216   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2217   smallLayout = sizeInfo[boardSize].smallLayout;\r
2218   squareSize = sizeInfo[boardSize].squareSize;\r
2219   lineGap = sizeInfo[boardSize].lineGap;\r
2220   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2221 \r
2222   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2223       lineGap = appData.overrideLineGap;\r
2224   }\r
2225 \r
2226   if (tinyLayout != oldTinyLayout) {\r
2227     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2228     if (tinyLayout) {\r
2229       style &= ~WS_SYSMENU;\r
2230       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2231                  "&Minimize\tCtrl+F4");\r
2232     } else {\r
2233       style |= WS_SYSMENU;\r
2234       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2235     }\r
2236     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2237 \r
2238     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2239       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2240         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2241     }\r
2242     DrawMenuBar(hwndMain);\r
2243   }\r
2244 \r
2245   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2246   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2247 \r
2248   /* Get text area sizes */\r
2249   hdc = GetDC(hwndMain);\r
2250   if (appData.clockMode) {\r
2251     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2252   } else {\r
2253     snprintf(buf, MSG_SIZ, _("White"));\r
2254   }\r
2255   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2256   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2257   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2258   str = _("We only care about the height here");\r
2259   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2260   SelectObject(hdc, oldFont);\r
2261   ReleaseDC(hwndMain, hdc);\r
2262 \r
2263   /* Compute where everything goes */\r
2264   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2265         /* [HGM] logo: if either logo is on, reserve space for it */\r
2266         logoHeight =  2*clockSize.cy;\r
2267         leftLogoRect.left   = OUTER_MARGIN;\r
2268         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2269         leftLogoRect.top    = OUTER_MARGIN;\r
2270         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2271 \r
2272         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2273         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2274         rightLogoRect.top    = OUTER_MARGIN;\r
2275         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2276 \r
2277 \r
2278     whiteRect.left = leftLogoRect.right;\r
2279     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2280     whiteRect.top = OUTER_MARGIN;\r
2281     whiteRect.bottom = whiteRect.top + logoHeight;\r
2282 \r
2283     blackRect.right = rightLogoRect.left;\r
2284     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2285     blackRect.top = whiteRect.top;\r
2286     blackRect.bottom = whiteRect.bottom;\r
2287   } else {\r
2288     whiteRect.left = OUTER_MARGIN;\r
2289     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2290     whiteRect.top = OUTER_MARGIN;\r
2291     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2292 \r
2293     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2294     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2295     blackRect.top = whiteRect.top;\r
2296     blackRect.bottom = whiteRect.bottom;\r
2297 \r
2298     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2299   }\r
2300 \r
2301   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2302   if (appData.showButtonBar) {\r
2303     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2304       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2305   } else {\r
2306     messageRect.right = OUTER_MARGIN + boardWidth;\r
2307   }\r
2308   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2309   messageRect.bottom = messageRect.top + messageSize.cy;\r
2310 \r
2311   boardRect.left = OUTER_MARGIN;\r
2312   boardRect.right = boardRect.left + boardWidth;\r
2313   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2314   boardRect.bottom = boardRect.top + boardHeight;\r
2315 \r
2316   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2317   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2318   oldBoardSize = boardSize;\r
2319   oldTinyLayout = tinyLayout;\r
2320   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2321   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2322     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2323   winW *= 1 + twoBoards;\r
2324   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2325   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2326   wpMain.height = winH; //       without disturbing window attachments\r
2327   GetWindowRect(hwndMain, &wrect);\r
2328   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2329                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2330 \r
2331   // [HGM] placement: let attached windows follow size change.\r
2332   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2333   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2334   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2335   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2336   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2337 \r
2338   /* compensate if menu bar wrapped */\r
2339   GetClientRect(hwndMain, &crect);\r
2340   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2341   wpMain.height += offby;\r
2342   switch (flags) {\r
2343   case WMSZ_TOPLEFT:\r
2344     SetWindowPos(hwndMain, NULL, \r
2345                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2346                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2347     break;\r
2348 \r
2349   case WMSZ_TOPRIGHT:\r
2350   case WMSZ_TOP:\r
2351     SetWindowPos(hwndMain, NULL, \r
2352                  wrect.left, wrect.bottom - wpMain.height, \r
2353                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2354     break;\r
2355 \r
2356   case WMSZ_BOTTOMLEFT:\r
2357   case WMSZ_LEFT:\r
2358     SetWindowPos(hwndMain, NULL, \r
2359                  wrect.right - wpMain.width, wrect.top, \r
2360                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2361     break;\r
2362 \r
2363   case WMSZ_BOTTOMRIGHT:\r
2364   case WMSZ_BOTTOM:\r
2365   case WMSZ_RIGHT:\r
2366   default:\r
2367     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2368                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2369     break;\r
2370   }\r
2371 \r
2372   hwndPause = NULL;\r
2373   for (i = 0; i < N_BUTTONS; i++) {\r
2374     if (buttonDesc[i].hwnd != NULL) {\r
2375       DestroyWindow(buttonDesc[i].hwnd);\r
2376       buttonDesc[i].hwnd = NULL;\r
2377     }\r
2378     if (appData.showButtonBar) {\r
2379       buttonDesc[i].hwnd =\r
2380         CreateWindow("BUTTON", buttonDesc[i].label,\r
2381                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2382                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2383                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2384                      (HMENU) buttonDesc[i].id,\r
2385                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2386       if (tinyLayout) {\r
2387         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2388                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2389                     MAKELPARAM(FALSE, 0));\r
2390       }\r
2391       if (buttonDesc[i].id == IDM_Pause)\r
2392         hwndPause = buttonDesc[i].hwnd;\r
2393       buttonDesc[i].wndproc = (WNDPROC)\r
2394         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2395     }\r
2396   }\r
2397   if (gridPen != NULL) DeleteObject(gridPen);\r
2398   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2399   if (premovePen != NULL) DeleteObject(premovePen);\r
2400   if (lineGap != 0) {\r
2401     logbrush.lbStyle = BS_SOLID;\r
2402     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2403     gridPen =\r
2404       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2405                    lineGap, &logbrush, 0, NULL);\r
2406     logbrush.lbColor = highlightSquareColor;\r
2407     highlightPen =\r
2408       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2409                    lineGap, &logbrush, 0, NULL);\r
2410 \r
2411     logbrush.lbColor = premoveHighlightColor; \r
2412     premovePen =\r
2413       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2414                    lineGap, &logbrush, 0, NULL);\r
2415 \r
2416     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2417     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2418       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2419       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2420         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2421       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2422         BOARD_WIDTH * (squareSize + lineGap);\r
2423       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2424     }\r
2425     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2426       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2427       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2428         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2429         lineGap / 2 + (i * (squareSize + lineGap));\r
2430       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2431         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2432       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2433     }\r
2434   }\r
2435 \r
2436   /* [HGM] Licensing requirement */\r
2437 #ifdef GOTHIC\r
2438   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2439 #endif\r
2440 #ifdef FALCON\r
2441   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2442 #endif\r
2443   GothicPopUp( "", VariantNormal);\r
2444 \r
2445 \r
2446 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2447 \r
2448   /* Load piece bitmaps for this board size */\r
2449   for (i=0; i<=2; i++) {\r
2450     for (piece = WhitePawn;\r
2451          (int) piece < (int) BlackPawn;\r
2452          piece = (ChessSquare) ((int) piece + 1)) {\r
2453       if (pieceBitmap[i][piece] != NULL)\r
2454         DeleteObject(pieceBitmap[i][piece]);\r
2455     }\r
2456   }\r
2457 \r
2458   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2459   // Orthodox Chess pieces\r
2460   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2461   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2462   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2463   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2464   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2465   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2466   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2467   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2468   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2469   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2470   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2471   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2472   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2473   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2474   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2475   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2476     // in Shogi, Hijack the unused Queen for Lance\r
2477     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2478     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2479     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2480   } else {\r
2481     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2482     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2483     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2484   }\r
2485 \r
2486   if(squareSize <= 72 && squareSize >= 33) { \r
2487     /* A & C are available in most sizes now */\r
2488     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2489       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2490       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2491       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2492       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2493       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2494       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2495       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2496       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2497       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2498       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2499       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2500       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2501     } else { // Smirf-like\r
2502       if(gameInfo.variant == VariantSChess) {\r
2503         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2504         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2505         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2506       } else {\r
2507         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2508         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2509         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2510       }\r
2511     }\r
2512     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2513       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2514       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2515       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2516     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2517       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2518       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2519       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2520     } else { // WinBoard standard\r
2521       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2522       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2523       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2524     }\r
2525   }\r
2526 \r
2527 \r
2528   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2529     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2530     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2531     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2532     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2533     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2534     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2535     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2536     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2537     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2538     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2539     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2540     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2541     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2542     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2543     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2544     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2545     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2546     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2547     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2548     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2549     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2550     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2551     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2552     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2553     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2554     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2555     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2556     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2557     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2558     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2559 \r
2560     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2561       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2562       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2563       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2564       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2565       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2566       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2567       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2568       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2569       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2570       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2571       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2572       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2573     } else {\r
2574       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2575       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2576       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2577       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2578       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2579       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2580       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2581       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2582       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2583       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2584       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2585       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2586     }\r
2587 \r
2588   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2589     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2590     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2591     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2592     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2593     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2594     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2595     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2596     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2597     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2598     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2599     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2600     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2601     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2602     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2603   }\r
2604 \r
2605 \r
2606   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2607   /* special Shogi support in this size */\r
2608   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2609       for (piece = WhitePawn;\r
2610            (int) piece < (int) BlackPawn;\r
2611            piece = (ChessSquare) ((int) piece + 1)) {\r
2612         if (pieceBitmap[i][piece] != NULL)\r
2613           DeleteObject(pieceBitmap[i][piece]);\r
2614       }\r
2615     }\r
2616   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2617   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2618   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2619   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2620   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2621   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2622   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2623   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2624   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2625   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2626   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2627   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2628   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2629   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2630   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2631   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2632   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2633   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2634   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2635   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2636   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2637   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2638   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2639   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2640   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2641   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2642   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2643   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2644   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2645   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2646   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2647   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2648   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2649   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2650   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2651   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2652   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2653   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2654   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2655   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2656   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2657   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2658   minorSize = 0;\r
2659   }\r
2660 }\r
2661 \r
2662 HBITMAP\r
2663 PieceBitmap(ChessSquare p, int kind)\r
2664 {\r
2665   if ((int) p >= (int) BlackPawn)\r
2666     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2667 \r
2668   return pieceBitmap[kind][(int) p];\r
2669 }\r
2670 \r
2671 /***************************************************************/\r
2672 \r
2673 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2674 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2675 /*\r
2676 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2677 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2678 */\r
2679 \r
2680 VOID\r
2681 SquareToPos(int row, int column, int * x, int * y)\r
2682 {\r
2683   if (flipView) {\r
2684     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2685     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2686   } else {\r
2687     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2688     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2689   }\r
2690 }\r
2691 \r
2692 VOID\r
2693 DrawCoordsOnDC(HDC hdc)\r
2694 {\r
2695   static char files[24] = {'0', '1','2','3','4','5','6','7','8','9','0','1','1','0','9','8','7','6','5','4','3','2','1','0'};\r
2696   static char ranks[24] = {'l', 'k','j','i','h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h','i','j','k','l'};\r
2697   char str[2] = { NULLCHAR, NULLCHAR };\r
2698   int oldMode, oldAlign, x, y, start, i;\r
2699   HFONT oldFont;\r
2700   HBRUSH oldBrush;\r
2701 \r
2702   if (!appData.showCoords)\r
2703     return;\r
2704 \r
2705   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
2706 \r
2707   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2708   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2709   oldAlign = GetTextAlign(hdc);\r
2710   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2711 \r
2712   y = boardRect.top + lineGap;\r
2713   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2714 \r
2715   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2716   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2717     str[0] = files[start + i];\r
2718     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2719     y += squareSize + lineGap;\r
2720   }\r
2721 \r
2722   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
2723 \r
2724   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2725   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2726     str[0] = ranks[start + i];\r
2727     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2728     x += squareSize + lineGap;\r
2729   }    \r
2730 \r
2731   SelectObject(hdc, oldBrush);\r
2732   SetBkMode(hdc, oldMode);\r
2733   SetTextAlign(hdc, oldAlign);\r
2734   SelectObject(hdc, oldFont);\r
2735 }\r
2736 \r
2737 VOID\r
2738 DrawGridOnDC(HDC hdc)\r
2739 {\r
2740   HPEN oldPen;\r
2741  \r
2742   if (lineGap != 0) {\r
2743     oldPen = SelectObject(hdc, gridPen);\r
2744     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2745     SelectObject(hdc, oldPen);\r
2746   }\r
2747 }\r
2748 \r
2749 #define HIGHLIGHT_PEN 0\r
2750 #define PREMOVE_PEN   1\r
2751 \r
2752 VOID\r
2753 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2754 {\r
2755   int x1, y1;\r
2756   HPEN oldPen, hPen;\r
2757   if (lineGap == 0) return;\r
2758   if (flipView) {\r
2759     x1 = boardRect.left +\r
2760       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2761     y1 = boardRect.top +\r
2762       lineGap/2 + y * (squareSize + lineGap);\r
2763   } else {\r
2764     x1 = boardRect.left +\r
2765       lineGap/2 + x * (squareSize + lineGap);\r
2766     y1 = boardRect.top +\r
2767       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2768   }\r
2769   hPen = pen ? premovePen : highlightPen;\r
2770   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2771   MoveToEx(hdc, x1, y1, NULL);\r
2772   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2773   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2774   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2775   LineTo(hdc, x1, y1);\r
2776   SelectObject(hdc, oldPen);\r
2777 }\r
2778 \r
2779 VOID\r
2780 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2781 {\r
2782   int i;\r
2783   for (i=0; i<2; i++) {\r
2784     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2785       DrawHighlightOnDC(hdc, TRUE,\r
2786                         h->sq[i].x, h->sq[i].y,\r
2787                         pen);\r
2788   }\r
2789 }\r
2790 \r
2791 /* Note: sqcolor is used only in monoMode */\r
2792 /* Note that this code is largely duplicated in woptions.c,\r
2793    function DrawSampleSquare, so that needs to be updated too */\r
2794 VOID\r
2795 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2796 {\r
2797   HBITMAP oldBitmap;\r
2798   HBRUSH oldBrush;\r
2799   int tmpSize;\r
2800 \r
2801   if (appData.blindfold) return;\r
2802 \r
2803   /* [AS] Use font-based pieces if needed */\r
2804   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2805     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2806     CreatePiecesFromFont();\r
2807 \r
2808     if( fontBitmapSquareSize == squareSize ) {\r
2809         int index = TranslatePieceToFontPiece(piece);\r
2810 \r
2811         SelectObject( tmphdc, hPieceMask[ index ] );\r
2812 \r
2813       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2814         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2815       else\r
2816         BitBlt( hdc,\r
2817             x, y,\r
2818             squareSize, squareSize,\r
2819             tmphdc,\r
2820             0, 0,\r
2821             SRCAND );\r
2822 \r
2823         SelectObject( tmphdc, hPieceFace[ index ] );\r
2824 \r
2825       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2826         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2827       else\r
2828         BitBlt( hdc,\r
2829             x, y,\r
2830             squareSize, squareSize,\r
2831             tmphdc,\r
2832             0, 0,\r
2833             SRCPAINT );\r
2834 \r
2835         return;\r
2836     }\r
2837   }\r
2838 \r
2839   if (appData.monoMode) {\r
2840     SelectObject(tmphdc, PieceBitmap(piece, \r
2841       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2842     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2843            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2844   } else {\r
2845     tmpSize = squareSize;\r
2846     if(minorSize &&\r
2847         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2848          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2849       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2850       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2851       x += (squareSize - minorSize)>>1;\r
2852       y += squareSize - minorSize - 2;\r
2853       tmpSize = minorSize;\r
2854     }\r
2855     if (color || appData.allWhite ) {\r
2856       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2857       if( color )\r
2858               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2859       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2860       if(appData.upsideDown && color==flipView)\r
2861         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2862       else\r
2863         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2864       /* Use black for outline of white pieces */\r
2865       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2866       if(appData.upsideDown && color==flipView)\r
2867         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2868       else\r
2869         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2870     } else {\r
2871       /* Use square color for details of black pieces */\r
2872       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2873       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2874       if(appData.upsideDown && !flipView)\r
2875         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2876       else\r
2877         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2878     }\r
2879     SelectObject(hdc, oldBrush);\r
2880     SelectObject(tmphdc, oldBitmap);\r
2881   }\r
2882 }\r
2883 \r
2884 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2885 int GetBackTextureMode( int algo )\r
2886 {\r
2887     int result = BACK_TEXTURE_MODE_DISABLED;\r
2888 \r
2889     switch( algo ) \r
2890     {\r
2891         case BACK_TEXTURE_MODE_PLAIN:\r
2892             result = 1; /* Always use identity map */\r
2893             break;\r
2894         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2895             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2896             break;\r
2897     }\r
2898 \r
2899     return result;\r
2900 }\r
2901 \r
2902 /* \r
2903     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2904     to handle redraws cleanly (as random numbers would always be different).\r
2905 */\r
2906 VOID RebuildTextureSquareInfo()\r
2907 {\r
2908     BITMAP bi;\r
2909     int lite_w = 0;\r
2910     int lite_h = 0;\r
2911     int dark_w = 0;\r
2912     int dark_h = 0;\r
2913     int row;\r
2914     int col;\r
2915 \r
2916     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2917 \r
2918     if( liteBackTexture != NULL ) {\r
2919         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2920             lite_w = bi.bmWidth;\r
2921             lite_h = bi.bmHeight;\r
2922         }\r
2923     }\r
2924 \r
2925     if( darkBackTexture != NULL ) {\r
2926         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2927             dark_w = bi.bmWidth;\r
2928             dark_h = bi.bmHeight;\r
2929         }\r
2930     }\r
2931 \r
2932     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2933         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2934             if( (col + row) & 1 ) {\r
2935                 /* Lite square */\r
2936                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2937                   if( lite_w >= squareSize*BOARD_WIDTH )\r
2938                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
2939                   else\r
2940                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2941                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
2942                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
2943                   else\r
2944                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2945                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2946                 }\r
2947             }\r
2948             else {\r
2949                 /* Dark square */\r
2950                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
2951                   if( dark_w >= squareSize*BOARD_WIDTH )\r
2952                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
2953                   else\r
2954                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
2955                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
2956                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
2957                   else\r
2958                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
2959                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
2960                 }\r
2961             }\r
2962         }\r
2963     }\r
2964 }\r
2965 \r
2966 /* [AS] Arrow highlighting support */\r
2967 \r
2968 static double A_WIDTH = 5; /* Width of arrow body */\r
2969 \r
2970 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
2971 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
2972 \r
2973 static double Sqr( double x )\r
2974 {\r
2975     return x*x;\r
2976 }\r
2977 \r
2978 static int Round( double x )\r
2979 {\r
2980     return (int) (x + 0.5);\r
2981 }\r
2982 \r
2983 /* Draw an arrow between two points using current settings */\r
2984 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
2985 {\r
2986     POINT arrow[7];\r
2987     double dx, dy, j, k, x, y;\r
2988 \r
2989     if( d_x == s_x ) {\r
2990         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2991 \r
2992         arrow[0].x = s_x + A_WIDTH + 0.5;\r
2993         arrow[0].y = s_y;\r
2994 \r
2995         arrow[1].x = s_x + A_WIDTH + 0.5;\r
2996         arrow[1].y = d_y - h;\r
2997 \r
2998         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
2999         arrow[2].y = d_y - h;\r
3000 \r
3001         arrow[3].x = d_x;\r
3002         arrow[3].y = d_y;\r
3003 \r
3004         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3005         arrow[5].y = d_y - h;\r
3006 \r
3007         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3008         arrow[4].y = d_y - h;\r
3009 \r
3010         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3011         arrow[6].y = s_y;\r
3012     }\r
3013     else if( d_y == s_y ) {\r
3014         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3015 \r
3016         arrow[0].x = s_x;\r
3017         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3018 \r
3019         arrow[1].x = d_x - w;\r
3020         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3021 \r
3022         arrow[2].x = d_x - w;\r
3023         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3024 \r
3025         arrow[3].x = d_x;\r
3026         arrow[3].y = d_y;\r
3027 \r
3028         arrow[5].x = d_x - w;\r
3029         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3030 \r
3031         arrow[4].x = d_x - w;\r
3032         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3033 \r
3034         arrow[6].x = s_x;\r
3035         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3036     }\r
3037     else {\r
3038         /* [AS] Needed a lot of paper for this! :-) */\r
3039         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3040         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3041   \r
3042         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3043 \r
3044         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3045 \r
3046         x = s_x;\r
3047         y = s_y;\r
3048 \r
3049         arrow[0].x = Round(x - j);\r
3050         arrow[0].y = Round(y + j*dx);\r
3051 \r
3052         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3053         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3054 \r
3055         if( d_x > s_x ) {\r
3056             x = (double) d_x - k;\r
3057             y = (double) d_y - k*dy;\r
3058         }\r
3059         else {\r
3060             x = (double) d_x + k;\r
3061             y = (double) d_y + k*dy;\r
3062         }\r
3063 \r
3064         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3065 \r
3066         arrow[6].x = Round(x - j);\r
3067         arrow[6].y = Round(y + j*dx);\r
3068 \r
3069         arrow[2].x = Round(arrow[6].x + 2*j);\r
3070         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3071 \r
3072         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3073         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3074 \r
3075         arrow[4].x = d_x;\r
3076         arrow[4].y = d_y;\r
3077 \r
3078         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3079         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3080     }\r
3081 \r
3082     Polygon( hdc, arrow, 7 );\r
3083 }\r
3084 \r
3085 /* [AS] Draw an arrow between two squares */\r
3086 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3087 {\r
3088     int s_x, s_y, d_x, d_y;\r
3089     HPEN hpen;\r
3090     HPEN holdpen;\r
3091     HBRUSH hbrush;\r
3092     HBRUSH holdbrush;\r
3093     LOGBRUSH stLB;\r
3094 \r
3095     if( s_col == d_col && s_row == d_row ) {\r
3096         return;\r
3097     }\r
3098 \r
3099     /* Get source and destination points */\r
3100     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3101     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3102 \r
3103     if( d_y > s_y ) {\r
3104         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3105     }\r
3106     else if( d_y < s_y ) {\r
3107         d_y += squareSize / 2 + squareSize / 4;\r
3108     }\r
3109     else {\r
3110         d_y += squareSize / 2;\r
3111     }\r
3112 \r
3113     if( d_x > s_x ) {\r
3114         d_x += squareSize / 2 - squareSize / 4;\r
3115     }\r
3116     else if( d_x < s_x ) {\r
3117         d_x += squareSize / 2 + squareSize / 4;\r
3118     }\r
3119     else {\r
3120         d_x += squareSize / 2;\r
3121     }\r
3122 \r
3123     s_x += squareSize / 2;\r
3124     s_y += squareSize / 2;\r
3125 \r
3126     /* Adjust width */\r
3127     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3128 \r
3129     /* Draw */\r
3130     stLB.lbStyle = BS_SOLID;\r
3131     stLB.lbColor = appData.highlightArrowColor;\r
3132     stLB.lbHatch = 0;\r
3133 \r
3134     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3135     holdpen = SelectObject( hdc, hpen );\r
3136     hbrush = CreateBrushIndirect( &stLB );\r
3137     holdbrush = SelectObject( hdc, hbrush );\r
3138 \r
3139     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3140 \r
3141     SelectObject( hdc, holdpen );\r
3142     SelectObject( hdc, holdbrush );\r
3143     DeleteObject( hpen );\r
3144     DeleteObject( hbrush );\r
3145 }\r
3146 \r
3147 BOOL HasHighlightInfo()\r
3148 {\r
3149     BOOL result = FALSE;\r
3150 \r
3151     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3152         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3153     {\r
3154         result = TRUE;\r
3155     }\r
3156 \r
3157     return result;\r
3158 }\r
3159 \r
3160 BOOL IsDrawArrowEnabled()\r
3161 {\r
3162     BOOL result = FALSE;\r
3163 \r
3164     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3165         result = TRUE;\r
3166     }\r
3167 \r
3168     return result;\r
3169 }\r
3170 \r
3171 VOID DrawArrowHighlight( HDC hdc )\r
3172 {\r
3173     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3174         DrawArrowBetweenSquares( hdc,\r
3175             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3176             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3177     }\r
3178 }\r
3179 \r
3180 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3181 {\r
3182     HRGN result = NULL;\r
3183 \r
3184     if( HasHighlightInfo() ) {\r
3185         int x1, y1, x2, y2;\r
3186         int sx, sy, dx, dy;\r
3187 \r
3188         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3189         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3190 \r
3191         sx = MIN( x1, x2 );\r
3192         sy = MIN( y1, y2 );\r
3193         dx = MAX( x1, x2 ) + squareSize;\r
3194         dy = MAX( y1, y2 ) + squareSize;\r
3195 \r
3196         result = CreateRectRgn( sx, sy, dx, dy );\r
3197     }\r
3198 \r
3199     return result;\r
3200 }\r
3201 \r
3202 /*\r
3203     Warning: this function modifies the behavior of several other functions. \r
3204     \r
3205     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3206     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3207     repaint is scattered all over the place, which is not good for features such as\r
3208     "arrow highlighting" that require a full repaint of the board.\r
3209 \r
3210     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3211     user interaction, when speed is not so important) but especially to avoid errors\r
3212     in the displayed graphics.\r
3213 \r
3214     In such patched places, I always try refer to this function so there is a single\r
3215     place to maintain knowledge.\r
3216     \r
3217     To restore the original behavior, just return FALSE unconditionally.\r
3218 */\r
3219 BOOL IsFullRepaintPreferrable()\r
3220 {\r
3221     BOOL result = FALSE;\r
3222 \r
3223     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3224         /* Arrow may appear on the board */\r
3225         result = TRUE;\r
3226     }\r
3227 \r
3228     return result;\r
3229 }\r
3230 \r
3231 /* \r
3232     This function is called by DrawPosition to know whether a full repaint must\r
3233     be forced or not.\r
3234 \r
3235     Only DrawPosition may directly call this function, which makes use of \r
3236     some state information. Other function should call DrawPosition specifying \r
3237     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3238 */\r
3239 BOOL DrawPositionNeedsFullRepaint()\r
3240 {\r
3241     BOOL result = FALSE;\r
3242 \r
3243     /* \r
3244         Probably a slightly better policy would be to trigger a full repaint\r
3245         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3246         but animation is fast enough that it's difficult to notice.\r
3247     */\r
3248     if( animInfo.piece == EmptySquare ) {\r
3249         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3250             result = TRUE;\r
3251         }\r
3252     }\r
3253 \r
3254     return result;\r
3255 }\r
3256 \r
3257 VOID\r
3258 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3259 {\r
3260   int row, column, x, y, square_color, piece_color;\r
3261   ChessSquare piece;\r
3262   HBRUSH oldBrush;\r
3263   HDC texture_hdc = NULL;\r
3264 \r
3265   /* [AS] Initialize background textures if needed */\r
3266   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3267       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3268       if( backTextureSquareSize != squareSize \r
3269        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3270           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3271           backTextureSquareSize = squareSize;\r
3272           RebuildTextureSquareInfo();\r
3273       }\r
3274 \r
3275       texture_hdc = CreateCompatibleDC( hdc );\r
3276   }\r
3277 \r
3278   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3279     for (column = 0; column < BOARD_WIDTH; column++) {\r
3280   \r
3281       SquareToPos(row, column, &x, &y);\r
3282 \r
3283       piece = board[row][column];\r
3284 \r
3285       square_color = ((column + row) % 2) == 1;\r
3286       if( gameInfo.variant == VariantXiangqi ) {\r
3287           square_color = !InPalace(row, column);\r
3288           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3289           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3290       }\r
3291       piece_color = (int) piece < (int) BlackPawn;\r
3292 \r
3293 \r
3294       /* [HGM] holdings file: light square or black */\r
3295       if(column == BOARD_LEFT-2) {\r
3296             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3297                 square_color = 1;\r
3298             else {\r
3299                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3300                 continue;\r
3301             }\r
3302       } else\r
3303       if(column == BOARD_RGHT + 1 ) {\r
3304             if( row < gameInfo.holdingsSize )\r
3305                 square_color = 1;\r
3306             else {\r
3307                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3308                 continue;\r
3309             }\r
3310       }\r
3311       if(column == BOARD_LEFT-1 ) /* left align */\r
3312             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3313       else if( column == BOARD_RGHT) /* right align */\r
3314             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3315       else\r
3316       if (appData.monoMode) {\r
3317         if (piece == EmptySquare) {\r
3318           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3319                  square_color ? WHITENESS : BLACKNESS);\r
3320         } else {\r
3321           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3322         }\r
3323       } \r
3324       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
3325           /* [AS] Draw the square using a texture bitmap */\r
3326           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3327           int r = row, c = column; // [HGM] do not flip board in flipView\r
3328           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3329 \r
3330           DrawTile( x, y, \r
3331               squareSize, squareSize, \r
3332               hdc, \r
3333               texture_hdc,\r
3334               backTextureSquareInfo[r][c].mode,\r
3335               backTextureSquareInfo[r][c].x,\r
3336               backTextureSquareInfo[r][c].y );\r
3337 \r
3338           SelectObject( texture_hdc, hbm );\r
3339 \r
3340           if (piece != EmptySquare) {\r
3341               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3342           }\r
3343       }\r
3344       else {\r
3345         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3346 \r
3347         oldBrush = SelectObject(hdc, brush );\r
3348         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3349         SelectObject(hdc, oldBrush);\r
3350         if (piece != EmptySquare)\r
3351           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3352       }\r
3353     }\r
3354   }\r
3355 \r
3356   if( texture_hdc != NULL ) {\r
3357     DeleteDC( texture_hdc );\r
3358   }\r
3359 }\r
3360 \r
3361 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3362 void fputDW(FILE *f, int x)\r
3363 {\r
3364         fputc(x     & 255, f);\r
3365         fputc(x>>8  & 255, f);\r
3366         fputc(x>>16 & 255, f);\r
3367         fputc(x>>24 & 255, f);\r
3368 }\r
3369 \r
3370 #define MAX_CLIPS 200   /* more than enough */\r
3371 \r
3372 VOID\r
3373 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3374 {\r
3375 //  HBITMAP bufferBitmap;\r
3376   BITMAP bi;\r
3377 //  RECT Rect;\r
3378   HDC tmphdc;\r
3379   HBITMAP hbm;\r
3380   int w = 100, h = 50;\r
3381 \r
3382   if(logo == NULL) return;\r
3383 //  GetClientRect(hwndMain, &Rect);\r
3384 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3385 //                                      Rect.bottom-Rect.top+1);\r
3386   tmphdc = CreateCompatibleDC(hdc);\r
3387   hbm = SelectObject(tmphdc, logo);\r
3388   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3389             w = bi.bmWidth;\r
3390             h = bi.bmHeight;\r
3391   }\r
3392   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3393                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3394   SelectObject(tmphdc, hbm);\r
3395   DeleteDC(tmphdc);\r
3396 }\r
3397 \r
3398 VOID\r
3399 DisplayLogos()\r
3400 {\r
3401   if(logoHeight) {\r
3402         HDC hdc = GetDC(hwndMain);\r
3403         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3404         if(appData.autoLogo) {\r
3405           \r
3406           switch(gameMode) { // pick logos based on game mode\r
3407             case IcsObserving:\r
3408                 whiteLogo = second.programLogo; // ICS logo\r
3409                 blackLogo = second.programLogo;\r
3410             default:\r
3411                 break;\r
3412             case IcsPlayingWhite:\r
3413                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3414                 blackLogo = second.programLogo; // ICS logo\r
3415                 break;\r
3416             case IcsPlayingBlack:\r
3417                 whiteLogo = second.programLogo; // ICS logo\r
3418                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3419                 break;\r
3420             case TwoMachinesPlay:\r
3421                 if(first.twoMachinesColor[0] == 'b') {\r
3422                     whiteLogo = second.programLogo;\r
3423                     blackLogo = first.programLogo;\r
3424                 }\r
3425                 break;\r
3426             case MachinePlaysWhite:\r
3427                 blackLogo = userLogo;\r
3428                 break;\r
3429             case MachinePlaysBlack:\r
3430                 whiteLogo = userLogo;\r
3431                 blackLogo = first.programLogo;\r
3432           }\r
3433         }\r
3434         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3435         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3436         ReleaseDC(hwndMain, hdc);\r
3437   }\r
3438 }\r
3439 \r
3440 static HDC hdcSeek;\r
3441 \r
3442 // [HGM] seekgraph\r
3443 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3444 {\r
3445     POINT stPt;\r
3446     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3447     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3448     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3449     SelectObject( hdcSeek, hp );\r
3450 }\r
3451 \r
3452 // front-end wrapper for drawing functions to do rectangles\r
3453 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3454 {\r
3455     HPEN hp;\r
3456     RECT rc;\r
3457 \r
3458     if (hdcSeek == NULL) {\r
3459     hdcSeek = GetDC(hwndMain);\r
3460       if (!appData.monoMode) {\r
3461         SelectPalette(hdcSeek, hPal, FALSE);\r
3462         RealizePalette(hdcSeek);\r
3463       }\r
3464     }\r
3465     hp = SelectObject( hdcSeek, gridPen );\r
3466     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3467     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3468     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3469     SelectObject( hdcSeek, hp );\r
3470 }\r
3471 \r
3472 // front-end wrapper for putting text in graph\r
3473 void DrawSeekText(char *buf, int x, int y)\r
3474 {\r
3475         SIZE stSize;\r
3476         SetBkMode( hdcSeek, TRANSPARENT );\r
3477         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3478         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3479 }\r
3480 \r
3481 void DrawSeekDot(int x, int y, int color)\r
3482 {\r
3483         int square = color & 0x80;\r
3484         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3485                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3486         color &= 0x7F;\r
3487         if(square)\r
3488             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3489                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3490         else\r
3491             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3492                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3493             SelectObject(hdcSeek, oldBrush);\r
3494 }\r
3495 \r
3496 VOID\r
3497 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3498 {\r
3499   static Board lastReq[2], lastDrawn[2];\r
3500   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3501   static int lastDrawnFlipView = 0;\r
3502   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3503   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3504   HDC tmphdc;\r
3505   HDC hdcmem;\r
3506   HBITMAP bufferBitmap;\r
3507   HBITMAP oldBitmap;\r
3508   RECT Rect;\r
3509   HRGN clips[MAX_CLIPS];\r
3510   ChessSquare dragged_piece = EmptySquare;\r
3511   int nr = twoBoards*partnerUp;\r
3512 \r
3513   /* I'm undecided on this - this function figures out whether a full\r
3514    * repaint is necessary on its own, so there's no real reason to have the\r
3515    * caller tell it that.  I think this can safely be set to FALSE - but\r
3516    * if we trust the callers not to request full repaints unnessesarily, then\r
3517    * we could skip some clipping work.  In other words, only request a full\r
3518    * redraw when the majority of pieces have changed positions (ie. flip, \r
3519    * gamestart and similar)  --Hawk\r
3520    */\r
3521   Boolean fullrepaint = repaint;\r
3522 \r
3523   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3524 \r
3525   if( DrawPositionNeedsFullRepaint() ) {\r
3526       fullrepaint = TRUE;\r
3527   }\r
3528 \r
3529   if (board == NULL) {\r
3530     if (!lastReqValid[nr]) {\r
3531       return;\r
3532     }\r
3533     board = lastReq[nr];\r
3534   } else {\r
3535     CopyBoard(lastReq[nr], board);\r
3536     lastReqValid[nr] = 1;\r
3537   }\r
3538 \r
3539   if (doingSizing) {\r
3540     return;\r
3541   }\r
3542 \r
3543   if (IsIconic(hwndMain)) {\r
3544     return;\r
3545   }\r
3546 \r
3547   if (hdc == NULL) {\r
3548     hdc = GetDC(hwndMain);\r
3549     if (!appData.monoMode) {\r
3550       SelectPalette(hdc, hPal, FALSE);\r
3551       RealizePalette(hdc);\r
3552     }\r
3553     releaseDC = TRUE;\r
3554   } else {\r
3555     releaseDC = FALSE;\r
3556   }\r
3557 \r
3558   /* Create some work-DCs */\r
3559   hdcmem = CreateCompatibleDC(hdc);\r
3560   tmphdc = CreateCompatibleDC(hdc);\r
3561 \r
3562   /* If dragging is in progress, we temporarely remove the piece */\r
3563   /* [HGM] or temporarily decrease count if stacked              */\r
3564   /*       !! Moved to before board compare !!                   */\r
3565   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3566     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3567     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3568             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3569         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3570     } else \r
3571     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3572             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3573         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3574     } else \r
3575         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3576   }\r
3577 \r
3578   /* Figure out which squares need updating by comparing the \r
3579    * newest board with the last drawn board and checking if\r
3580    * flipping has changed.\r
3581    */\r
3582   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3583     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3584       for (column = 0; column < BOARD_WIDTH; column++) {\r
3585         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3586           SquareToPos(row, column, &x, &y);\r
3587           clips[num_clips++] =\r
3588             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3589         }\r
3590       }\r
3591     }\r
3592    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3593     for (i=0; i<2; i++) {\r
3594       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3595           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3596         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3597             lastDrawnHighlight.sq[i].y >= 0) {\r
3598           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3599                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3600           clips[num_clips++] =\r
3601             CreateRectRgn(x - lineGap, y - lineGap, \r
3602                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3603         }\r
3604         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3605           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3606           clips[num_clips++] =\r
3607             CreateRectRgn(x - lineGap, y - lineGap, \r
3608                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3609         }\r
3610       }\r
3611     }\r
3612     for (i=0; i<2; i++) {\r
3613       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3614           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3615         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3616             lastDrawnPremove.sq[i].y >= 0) {\r
3617           SquareToPos(lastDrawnPremove.sq[i].y,\r
3618                       lastDrawnPremove.sq[i].x, &x, &y);\r
3619           clips[num_clips++] =\r
3620             CreateRectRgn(x - lineGap, y - lineGap, \r
3621                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3622         }\r
3623         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3624             premoveHighlightInfo.sq[i].y >= 0) {\r
3625           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3626                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3627           clips[num_clips++] =\r
3628             CreateRectRgn(x - lineGap, y - lineGap, \r
3629                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3630         }\r
3631       }\r
3632     }\r
3633    } else { // nr == 1\r
3634         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3635         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3636         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3637         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3638       for (i=0; i<2; i++) {\r
3639         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3640             partnerHighlightInfo.sq[i].y >= 0) {\r
3641           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3642                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3643           clips[num_clips++] =\r
3644             CreateRectRgn(x - lineGap, y - lineGap, \r
3645                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3646         }\r
3647         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3648             oldPartnerHighlight.sq[i].y >= 0) {\r
3649           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3650                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3651           clips[num_clips++] =\r
3652             CreateRectRgn(x - lineGap, y - lineGap, \r
3653                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3654         }\r
3655       }\r
3656    }\r
3657   } else {\r
3658     fullrepaint = TRUE;\r
3659   }\r
3660 \r
3661   /* Create a buffer bitmap - this is the actual bitmap\r
3662    * being written to.  When all the work is done, we can\r
3663    * copy it to the real DC (the screen).  This avoids\r
3664    * the problems with flickering.\r
3665    */\r
3666   GetClientRect(hwndMain, &Rect);\r
3667   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3668                                         Rect.bottom-Rect.top+1);\r
3669   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3670   if (!appData.monoMode) {\r
3671     SelectPalette(hdcmem, hPal, FALSE);\r
3672   }\r
3673 \r
3674   /* Create clips for dragging */\r
3675   if (!fullrepaint) {\r
3676     if (dragInfo.from.x >= 0) {\r
3677       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3678       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3679     }\r
3680     if (dragInfo.start.x >= 0) {\r
3681       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3682       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3683     }\r
3684     if (dragInfo.pos.x >= 0) {\r
3685       x = dragInfo.pos.x - squareSize / 2;\r
3686       y = dragInfo.pos.y - squareSize / 2;\r
3687       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3688     }\r
3689     if (dragInfo.lastpos.x >= 0) {\r
3690       x = dragInfo.lastpos.x - squareSize / 2;\r
3691       y = dragInfo.lastpos.y - squareSize / 2;\r
3692       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3693     }\r
3694   }\r
3695 \r
3696   /* Are we animating a move?  \r
3697    * If so, \r
3698    *   - remove the piece from the board (temporarely)\r
3699    *   - calculate the clipping region\r
3700    */\r
3701   if (!fullrepaint) {\r
3702     if (animInfo.piece != EmptySquare) {\r
3703       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3704       x = boardRect.left + animInfo.lastpos.x;\r
3705       y = boardRect.top + animInfo.lastpos.y;\r
3706       x2 = boardRect.left + animInfo.pos.x;\r
3707       y2 = boardRect.top + animInfo.pos.y;\r
3708       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3709       /* Slight kludge.  The real problem is that after AnimateMove is\r
3710          done, the position on the screen does not match lastDrawn.\r
3711          This currently causes trouble only on e.p. captures in\r
3712          atomic, where the piece moves to an empty square and then\r
3713          explodes.  The old and new positions both had an empty square\r
3714          at the destination, but animation has drawn a piece there and\r
3715          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3716       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3717     }\r
3718   }\r
3719 \r
3720   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3721   if (num_clips == 0)\r
3722     fullrepaint = TRUE;\r
3723 \r
3724   /* Set clipping on the memory DC */\r
3725   if (!fullrepaint) {\r
3726     SelectClipRgn(hdcmem, clips[0]);\r
3727     for (x = 1; x < num_clips; x++) {\r
3728       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3729         abort();  // this should never ever happen!\r
3730     }\r
3731   }\r
3732 \r
3733   /* Do all the drawing to the memory DC */\r
3734   if(explodeInfo.radius) { // [HGM] atomic\r
3735         HBRUSH oldBrush;\r
3736         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3737         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3738         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3739         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3740         x += squareSize/2;\r
3741         y += squareSize/2;\r
3742         if(!fullrepaint) {\r
3743           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3744           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3745         }\r
3746         DrawGridOnDC(hdcmem);\r
3747         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3748         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3749         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3750         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3751         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3752         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3753         SelectObject(hdcmem, oldBrush);\r
3754   } else {\r
3755     DrawGridOnDC(hdcmem);\r
3756     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3757         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3758         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3759     } else {\r
3760         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3761         oldPartnerHighlight = partnerHighlightInfo;\r
3762     }\r
3763     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3764   }\r
3765   if(nr == 0) // [HGM] dual: markers only on left board\r
3766   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3767     for (column = 0; column < BOARD_WIDTH; column++) {\r
3768         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3769             HBRUSH oldBrush = SelectObject(hdcmem, \r
3770                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3771             SquareToPos(row, column, &x, &y);\r
3772             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3773                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3774             SelectObject(hdcmem, oldBrush);\r
3775         }\r
3776     }\r
3777   }\r
3778 \r
3779   if( appData.highlightMoveWithArrow ) {\r
3780     DrawArrowHighlight(hdcmem);\r
3781   }\r
3782 \r
3783   DrawCoordsOnDC(hdcmem);\r
3784 \r
3785   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3786                  /* to make sure lastDrawn contains what is actually drawn */\r
3787 \r
3788   /* Put the dragged piece back into place and draw it (out of place!) */\r
3789     if (dragged_piece != EmptySquare) {\r
3790     /* [HGM] or restack */\r
3791     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3792                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3793     else\r
3794     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3795                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3796     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3797     x = dragInfo.pos.x - squareSize / 2;\r
3798     y = dragInfo.pos.y - squareSize / 2;\r
3799     DrawPieceOnDC(hdcmem, dragged_piece,\r
3800                   ((int) dragged_piece < (int) BlackPawn), \r
3801                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3802   }   \r
3803   \r
3804   /* Put the animated piece back into place and draw it */\r
3805   if (animInfo.piece != EmptySquare) {\r
3806     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3807     x = boardRect.left + animInfo.pos.x;\r
3808     y = boardRect.top + animInfo.pos.y;\r
3809     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3810                   ((int) animInfo.piece < (int) BlackPawn),\r
3811                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3812   }\r
3813 \r
3814   /* Release the bufferBitmap by selecting in the old bitmap \r
3815    * and delete the memory DC\r
3816    */\r
3817   SelectObject(hdcmem, oldBitmap);\r
3818   DeleteDC(hdcmem);\r
3819 \r
3820   /* Set clipping on the target DC */\r
3821   if (!fullrepaint) {\r
3822     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3823         RECT rect;\r
3824         GetRgnBox(clips[x], &rect);\r
3825         DeleteObject(clips[x]);\r
3826         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3827                           rect.right + wpMain.width/2, rect.bottom);\r
3828     }\r
3829     SelectClipRgn(hdc, clips[0]);\r
3830     for (x = 1; x < num_clips; x++) {\r
3831       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3832         abort();   // this should never ever happen!\r
3833     } \r
3834   }\r
3835 \r
3836   /* Copy the new bitmap onto the screen in one go.\r
3837    * This way we avoid any flickering\r
3838    */\r
3839   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3840   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3841          boardRect.right - boardRect.left,\r
3842          boardRect.bottom - boardRect.top,\r
3843          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3844   if(saveDiagFlag) { \r
3845     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
3846     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3847 \r
3848     GetObject(bufferBitmap, sizeof(b), &b);\r
3849     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
3850         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3851         bih.biWidth = b.bmWidth;\r
3852         bih.biHeight = b.bmHeight;\r
3853         bih.biPlanes = 1;\r
3854         bih.biBitCount = b.bmBitsPixel;\r
3855         bih.biCompression = 0;\r
3856         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3857         bih.biXPelsPerMeter = 0;\r
3858         bih.biYPelsPerMeter = 0;\r
3859         bih.biClrUsed = 0;\r
3860         bih.biClrImportant = 0;\r
3861 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3862 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3863         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3864 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3865 \r
3866         wb = b.bmWidthBytes;\r
3867         // count colors\r
3868         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3869                 int k = ((int*) pData)[i];\r
3870                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3871                 if(j >= 16) break;\r
3872                 color[j] = k;\r
3873                 if(j >= nrColors) nrColors = j+1;\r
3874         }\r
3875         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3876                 INT p = 0;\r
3877                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3878                     for(w=0; w<(wb>>2); w+=2) {\r
3879                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3880                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3881                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3882                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3883                         pData[p++] = m | j<<4;\r
3884                     }\r
3885                     while(p&3) pData[p++] = 0;\r
3886                 }\r
3887                 fac = 3;\r
3888                 wb = ((wb+31)>>5)<<2;\r
3889         }\r
3890         // write BITMAPFILEHEADER\r
3891         fprintf(diagFile, "BM");\r
3892         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3893         fputDW(diagFile, 0);\r
3894         fputDW(diagFile, 0x36 + (fac?64:0));\r
3895         // write BITMAPINFOHEADER\r
3896         fputDW(diagFile, 40);\r
3897         fputDW(diagFile, b.bmWidth);\r
3898         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3899         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3900         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3901         fputDW(diagFile, 0);\r
3902         fputDW(diagFile, 0);\r
3903         fputDW(diagFile, 0);\r
3904         fputDW(diagFile, 0);\r
3905         fputDW(diagFile, 0);\r
3906         fputDW(diagFile, 0);\r
3907         // write color table\r
3908         if(fac)\r
3909         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3910         // write bitmap data\r
3911         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3912                 fputc(pData[i], diagFile);\r
3913      }\r
3914   }\r
3915 \r
3916   SelectObject(tmphdc, oldBitmap);\r
3917 \r
3918   /* Massive cleanup */\r
3919   for (x = 0; x < num_clips; x++)\r
3920     DeleteObject(clips[x]);\r
3921 \r
3922   DeleteDC(tmphdc);\r
3923   DeleteObject(bufferBitmap);\r
3924 \r
3925   if (releaseDC) \r
3926     ReleaseDC(hwndMain, hdc);\r
3927   \r
3928   if (lastDrawnFlipView != flipView && nr == 0) {\r
3929     if (flipView)\r
3930       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3931     else\r
3932       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3933   }\r
3934 \r
3935 /*  CopyBoard(lastDrawn, board);*/\r
3936   lastDrawnHighlight = highlightInfo;\r
3937   lastDrawnPremove   = premoveHighlightInfo;\r
3938   lastDrawnFlipView = flipView;\r
3939   lastDrawnValid[nr] = 1;\r
3940 }\r
3941 \r
3942 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3943 int\r
3944 SaveDiagram(f)\r
3945      FILE *f;\r
3946 {\r
3947     saveDiagFlag = 1; diagFile = f;\r
3948     HDCDrawPosition(NULL, TRUE, NULL);\r
3949 \r
3950     saveDiagFlag = 0;\r
3951 \r
3952 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
3953     \r
3954     fclose(f);\r
3955     return TRUE;\r
3956 }\r
3957 \r
3958 \r
3959 /*---------------------------------------------------------------------------*\\r
3960 | CLIENT PAINT PROCEDURE\r
3961 |   This is the main event-handler for the WM_PAINT message.\r
3962 |\r
3963 \*---------------------------------------------------------------------------*/\r
3964 VOID\r
3965 PaintProc(HWND hwnd)\r
3966 {\r
3967   HDC         hdc;\r
3968   PAINTSTRUCT ps;\r
3969   HFONT       oldFont;\r
3970 \r
3971   if((hdc = BeginPaint(hwnd, &ps))) {\r
3972     if (IsIconic(hwnd)) {\r
3973       DrawIcon(hdc, 2, 2, iconCurrent);\r
3974     } else {\r
3975       if (!appData.monoMode) {\r
3976         SelectPalette(hdc, hPal, FALSE);\r
3977         RealizePalette(hdc);\r
3978       }\r
3979       HDCDrawPosition(hdc, 1, NULL);\r
3980       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
3981         flipView = !flipView; partnerUp = !partnerUp;\r
3982         HDCDrawPosition(hdc, 1, NULL);\r
3983         flipView = !flipView; partnerUp = !partnerUp;\r
3984       }\r
3985       oldFont =\r
3986         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3987       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
3988                  ETO_CLIPPED|ETO_OPAQUE,\r
3989                  &messageRect, messageText, strlen(messageText), NULL);\r
3990       SelectObject(hdc, oldFont);\r
3991       DisplayBothClocks();\r
3992       DisplayLogos();\r
3993     }\r
3994     EndPaint(hwnd,&ps);\r
3995   }\r
3996 \r
3997   return;\r
3998 }\r
3999 \r
4000 \r
4001 /*\r
4002  * If the user selects on a border boundary, return -1; if off the board,\r
4003  *   return -2.  Otherwise map the event coordinate to the square.\r
4004  * The offset boardRect.left or boardRect.top must already have been\r
4005  *   subtracted from x.\r
4006  */\r
4007 int EventToSquare(x, limit)\r
4008      int x, limit;\r
4009 {\r
4010   if (x <= 0)\r
4011     return -2;\r
4012   if (x < lineGap)\r
4013     return -1;\r
4014   x -= lineGap;\r
4015   if ((x % (squareSize + lineGap)) >= squareSize)\r
4016     return -1;\r
4017   x /= (squareSize + lineGap);\r
4018     if (x >= limit)\r
4019     return -2;\r
4020   return x;\r
4021 }\r
4022 \r
4023 typedef struct {\r
4024   char piece;\r
4025   int command;\r
4026   char* name;\r
4027 } DropEnable;\r
4028 \r
4029 DropEnable dropEnables[] = {\r
4030   { 'P', DP_Pawn, N_("Pawn") },\r
4031   { 'N', DP_Knight, N_("Knight") },\r
4032   { 'B', DP_Bishop, N_("Bishop") },\r
4033   { 'R', DP_Rook, N_("Rook") },\r
4034   { 'Q', DP_Queen, N_("Queen") },\r
4035 };\r
4036 \r
4037 VOID\r
4038 SetupDropMenu(HMENU hmenu)\r
4039 {\r
4040   int i, count, enable;\r
4041   char *p;\r
4042   extern char white_holding[], black_holding[];\r
4043   char item[MSG_SIZ];\r
4044 \r
4045   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4046     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4047                dropEnables[i].piece);\r
4048     count = 0;\r
4049     while (p && *p++ == dropEnables[i].piece) count++;\r
4050       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4051     enable = count > 0 || !appData.testLegality\r
4052       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4053                       && !appData.icsActive);\r
4054     ModifyMenu(hmenu, dropEnables[i].command,\r
4055                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4056                dropEnables[i].command, item);\r
4057   }\r
4058 }\r
4059 \r
4060 void DragPieceBegin(int x, int y)\r
4061 {\r
4062       dragInfo.lastpos.x = boardRect.left + x;\r
4063       dragInfo.lastpos.y = boardRect.top + y;\r
4064       dragInfo.from.x = fromX;\r
4065       dragInfo.from.y = fromY;\r
4066       dragInfo.start = dragInfo.from;\r
4067       SetCapture(hwndMain);\r
4068 }\r
4069 \r
4070 void DragPieceEnd(int x, int y)\r
4071 {\r
4072     ReleaseCapture();\r
4073     dragInfo.start.x = dragInfo.start.y = -1;\r
4074     dragInfo.from = dragInfo.start;\r
4075     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4076 }\r
4077 \r
4078 /* Event handler for mouse messages */\r
4079 VOID\r
4080 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4081 {\r
4082   int x, y, menuNr;\r
4083   POINT pt;\r
4084   static int recursive = 0;\r
4085   HMENU hmenu;\r
4086   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4087   extern ChessSquare promoSweep;\r
4088 \r
4089   if (recursive) {\r
4090     if (message == WM_MBUTTONUP) {\r
4091       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4092          to the middle button: we simulate pressing the left button too!\r
4093          */\r
4094       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4095       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4096     }\r
4097     return;\r
4098   }\r
4099   recursive++;\r
4100   \r
4101   pt.x = LOWORD(lParam);\r
4102   pt.y = HIWORD(lParam);\r
4103   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4104   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4105   if (!flipView && y >= 0) {\r
4106     y = BOARD_HEIGHT - 1 - y;\r
4107   }\r
4108   if (flipView && x >= 0) {\r
4109     x = BOARD_WIDTH - 1 - x;\r
4110   }\r
4111 \r
4112   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4113 \r
4114   switch (message) {\r
4115   case WM_LBUTTONDOWN:\r
4116       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4117         ClockClick(flipClock);\r
4118       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4119         ClockClick(!flipClock);\r
4120       }\r
4121       dragInfo.start.x = dragInfo.start.y = -1;\r
4122       dragInfo.from = dragInfo.start;\r
4123     if(fromX == -1 && frozen) { // not sure where this is for\r
4124                 fromX = fromY = -1; \r
4125       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4126       break;\r
4127     }\r
4128       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4129       DrawPosition(TRUE, NULL);\r
4130     break;\r
4131 \r
4132   case WM_LBUTTONUP:\r
4133       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4134       DrawPosition(TRUE, NULL);\r
4135     break;\r
4136 \r
4137   case WM_MOUSEMOVE:\r
4138     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4139     if(promoSweep != EmptySquare && appData.sweepSelect) { PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top); break; }\r
4140     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4141     if ((appData.animateDragging || appData.highlightDragging)\r
4142         && (wParam & MK_LBUTTON)\r
4143         && dragInfo.from.x >= 0) \r
4144     {\r
4145       BOOL full_repaint = FALSE;\r
4146 \r
4147       if (appData.animateDragging) {\r
4148         dragInfo.pos = pt;\r
4149       }\r
4150       if (appData.highlightDragging) {\r
4151         SetHighlights(fromX, fromY, x, y);\r
4152         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4153             full_repaint = TRUE;\r
4154         }\r
4155       }\r
4156       \r
4157       DrawPosition( full_repaint, NULL);\r
4158       \r
4159       dragInfo.lastpos = dragInfo.pos;\r
4160     }\r
4161     break;\r
4162 \r
4163   case WM_MOUSEWHEEL: // [DM]\r
4164     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4165        /* Mouse Wheel is being rolled forward\r
4166         * Play moves forward\r
4167         */\r
4168        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4169                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4170        /* Mouse Wheel is being rolled backward\r
4171         * Play moves backward\r
4172         */\r
4173        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4174                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4175     }\r
4176     break;\r
4177 \r
4178   case WM_MBUTTONUP:\r
4179   case WM_RBUTTONUP:\r
4180     ReleaseCapture();\r
4181     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4182     break;\r
4183  \r
4184   case WM_MBUTTONDOWN:\r
4185   case WM_RBUTTONDOWN:\r
4186     ErrorPopDown();\r
4187     ReleaseCapture();\r
4188     fromX = fromY = -1;\r
4189     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4190     dragInfo.start.x = dragInfo.start.y = -1;\r
4191     dragInfo.from = dragInfo.start;\r
4192     dragInfo.lastpos = dragInfo.pos;\r
4193     if (appData.highlightDragging) {\r
4194       ClearHighlights();\r
4195     }\r
4196     if(y == -2) {\r
4197       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4198       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4199           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4200       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4201           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4202       }\r
4203       break;\r
4204     }\r
4205     DrawPosition(TRUE, NULL);\r
4206 \r
4207     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4208     switch (menuNr) {\r
4209     case 0:\r
4210       if (message == WM_MBUTTONDOWN) {\r
4211         buttonCount = 3;  /* even if system didn't think so */\r
4212         if (wParam & MK_SHIFT) \r
4213           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4214         else\r
4215           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4216       } else { /* message == WM_RBUTTONDOWN */\r
4217         /* Just have one menu, on the right button.  Windows users don't\r
4218            think to try the middle one, and sometimes other software steals\r
4219            it, or it doesn't really exist. */\r
4220         if(gameInfo.variant != VariantShogi)\r
4221             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4222         else\r
4223             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4224       }\r
4225       break;\r
4226     case 2:\r
4227       SetCapture(hwndMain);
4228       break;\r
4229     case 1:\r
4230       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4231       SetupDropMenu(hmenu);\r
4232       MenuPopup(hwnd, pt, hmenu, -1);\r
4233     default:\r
4234       break;\r
4235     }\r
4236     break;\r
4237   }\r
4238 \r
4239   recursive--;\r
4240 }\r
4241 \r
4242 /* Preprocess messages for buttons in main window */\r
4243 LRESULT CALLBACK\r
4244 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4245 {\r
4246   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4247   int i, dir;\r
4248 \r
4249   for (i=0; i<N_BUTTONS; i++) {\r
4250     if (buttonDesc[i].id == id) break;\r
4251   }\r
4252   if (i == N_BUTTONS) return 0;\r
4253   switch (message) {\r
4254   case WM_KEYDOWN:\r
4255     switch (wParam) {\r
4256     case VK_LEFT:\r
4257     case VK_RIGHT:\r
4258       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4259       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4260       return TRUE;\r
4261     }\r
4262     break;\r
4263   case WM_CHAR:\r
4264     switch (wParam) {\r
4265     case '\r':\r
4266       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4267       return TRUE;\r
4268     default:\r
4269       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4270         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4271         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4272         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4273         SetFocus(h);\r
4274         SendMessage(h, WM_CHAR, wParam, lParam);\r
4275         return TRUE;\r
4276       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4277         PopUpMoveDialog((char)wParam);\r
4278       }\r
4279       break;\r
4280     }\r
4281     break;\r
4282   }\r
4283   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4284 }\r
4285 \r
4286 /* Process messages for Promotion dialog box */\r
4287 LRESULT CALLBACK\r
4288 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4289 {\r
4290   char promoChar;\r
4291 \r
4292   switch (message) {\r
4293   case WM_INITDIALOG: /* message: initialize dialog box */\r
4294     /* Center the dialog over the application window */\r
4295     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4296     Translate(hDlg, DLG_PromotionKing);\r
4297     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4298       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4299        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4300        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4301                SW_SHOW : SW_HIDE);\r
4302     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4303     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4304        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4305          PieceToChar(WhiteAngel) != '~') ||\r
4306         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4307          PieceToChar(BlackAngel) != '~')   ) ?\r
4308                SW_SHOW : SW_HIDE);\r
4309     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4310        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4311          PieceToChar(WhiteMarshall) != '~') ||\r
4312         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4313          PieceToChar(BlackMarshall) != '~')   ) ?\r
4314                SW_SHOW : SW_HIDE);\r
4315     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4316     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4317        gameInfo.variant != VariantShogi ?\r
4318                SW_SHOW : SW_HIDE);\r
4319     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4320        gameInfo.variant != VariantShogi ?\r
4321                SW_SHOW : SW_HIDE);\r
4322     if(gameInfo.variant == VariantShogi) {\r
4323         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4324         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4325         SetWindowText(hDlg, "Promote?");\r
4326     }\r
4327     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4328        gameInfo.variant == VariantSuper ?\r
4329                SW_SHOW : SW_HIDE);\r
4330     return TRUE;\r
4331 \r
4332   case WM_COMMAND: /* message: received a command */\r
4333     switch (LOWORD(wParam)) {\r
4334     case IDCANCEL:\r
4335       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4336       ClearHighlights();\r
4337       DrawPosition(FALSE, NULL);\r
4338       return TRUE;\r
4339     case PB_King:\r
4340       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4341       break;\r
4342     case PB_Queen:\r
4343       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4344       break;\r
4345     case PB_Rook:\r
4346       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4347       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4348       break;\r
4349     case PB_Bishop:\r
4350       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4351       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4352       break;\r
4353     case PB_Chancellor:\r
4354       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4355       break;\r
4356     case PB_Archbishop:\r
4357       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4358       break;\r
4359     case PB_Knight:\r
4360       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4361       break;\r
4362     default:\r
4363       return FALSE;\r
4364     }\r
4365     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4366     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4367     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4368     fromX = fromY = -1;\r
4369     if (!appData.highlightLastMove) {\r
4370       ClearHighlights();\r
4371       DrawPosition(FALSE, NULL);\r
4372     }\r
4373     return TRUE;\r
4374   }\r
4375   return FALSE;\r
4376 }\r
4377 \r
4378 /* Pop up promotion dialog */\r
4379 VOID\r
4380 PromotionPopup(HWND hwnd)\r
4381 {\r
4382   FARPROC lpProc;\r
4383 \r
4384   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4385   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4386     hwnd, (DLGPROC)lpProc);\r
4387   FreeProcInstance(lpProc);\r
4388 }\r
4389 \r
4390 void\r
4391 PromotionPopUp()\r
4392 {\r
4393   DrawPosition(TRUE, NULL);\r
4394   PromotionPopup(hwndMain);\r
4395 }\r
4396 \r
4397 /* Toggle ShowThinking */\r
4398 VOID\r
4399 ToggleShowThinking()\r
4400 {\r
4401   appData.showThinking = !appData.showThinking;\r
4402   ShowThinkingEvent();\r
4403 }\r
4404 \r
4405 VOID\r
4406 LoadGameDialog(HWND hwnd, char* title)\r
4407 {\r
4408   UINT number = 0;\r
4409   FILE *f;\r
4410   char fileTitle[MSG_SIZ];\r
4411   f = OpenFileDialog(hwnd, "rb", "",\r
4412                      appData.oldSaveStyle ? "gam" : "pgn",\r
4413                      GAME_FILT,\r
4414                      title, &number, fileTitle, NULL);\r
4415   if (f != NULL) {\r
4416     cmailMsgLoaded = FALSE;\r
4417     if (number == 0) {\r
4418       int error = GameListBuild(f);\r
4419       if (error) {\r
4420         DisplayError(_("Cannot build game list"), error);\r
4421       } else if (!ListEmpty(&gameList) &&\r
4422                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4423         GameListPopUp(f, fileTitle);\r
4424         return;\r
4425       }\r
4426       GameListDestroy();\r
4427       number = 1;\r
4428     }\r
4429     LoadGame(f, number, fileTitle, FALSE);\r
4430   }\r
4431 }\r
4432 \r
4433 int get_term_width()\r
4434 {\r
4435     HDC hdc;\r
4436     TEXTMETRIC tm;\r
4437     RECT rc;\r
4438     HFONT hfont, hold_font;\r
4439     LOGFONT lf;\r
4440     HWND hText;\r
4441 \r
4442     if (hwndConsole)\r
4443         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4444     else\r
4445         return 79;\r
4446 \r
4447     // get the text metrics\r
4448     hdc = GetDC(hText);\r
4449     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4450     if (consoleCF.dwEffects & CFE_BOLD)\r
4451         lf.lfWeight = FW_BOLD;\r
4452     if (consoleCF.dwEffects & CFE_ITALIC)\r
4453         lf.lfItalic = TRUE;\r
4454     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4455         lf.lfStrikeOut = TRUE;\r
4456     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4457         lf.lfUnderline = TRUE;\r
4458     hfont = CreateFontIndirect(&lf);\r
4459     hold_font = SelectObject(hdc, hfont);\r
4460     GetTextMetrics(hdc, &tm);\r
4461     SelectObject(hdc, hold_font);\r
4462     DeleteObject(hfont);\r
4463     ReleaseDC(hText, hdc);\r
4464 \r
4465     // get the rectangle\r
4466     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4467 \r
4468     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4469 }\r
4470 \r
4471 void UpdateICSWidth(HWND hText)\r
4472 {\r
4473     LONG old_width, new_width;\r
4474 \r
4475     new_width = get_term_width(hText, FALSE);\r
4476     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4477     if (new_width != old_width)\r
4478     {\r
4479         ics_update_width(new_width);\r
4480         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4481     }\r
4482 }\r
4483 \r
4484 VOID\r
4485 ChangedConsoleFont()\r
4486 {\r
4487   CHARFORMAT cfmt;\r
4488   CHARRANGE tmpsel, sel;\r
4489   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4490   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4491   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4492   PARAFORMAT paraf;\r
4493 \r
4494   cfmt.cbSize = sizeof(CHARFORMAT);\r
4495   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4496     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4497                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4498   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4499    * size.  This was undocumented in the version of MSVC++ that I had\r
4500    * when I wrote the code, but is apparently documented now.\r
4501    */\r
4502   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4503   cfmt.bCharSet = f->lf.lfCharSet;\r
4504   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4505   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4506   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4507   /* Why are the following seemingly needed too? */\r
4508   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4509   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4510   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4511   tmpsel.cpMin = 0;\r
4512   tmpsel.cpMax = -1; /*999999?*/\r
4513   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4514   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4515   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4516    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4517    */\r
4518   paraf.cbSize = sizeof(paraf);\r
4519   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4520   paraf.dxStartIndent = 0;\r
4521   paraf.dxOffset = WRAP_INDENT;\r
4522   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4523   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4524   UpdateICSWidth(hText);\r
4525 }\r
4526 \r
4527 /*---------------------------------------------------------------------------*\\r
4528  *\r
4529  * Window Proc for main window\r
4530  *\r
4531 \*---------------------------------------------------------------------------*/\r
4532 \r
4533 /* Process messages for main window, etc. */\r
4534 LRESULT CALLBACK\r
4535 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4536 {\r
4537   FARPROC lpProc;\r
4538   int wmId, wmEvent;\r
4539   char *defName;\r
4540   FILE *f;\r
4541   UINT number;\r
4542   char fileTitle[MSG_SIZ];\r
4543   char buf[MSG_SIZ];\r
4544   static SnapData sd;\r
4545 \r
4546   switch (message) {\r
4547 \r
4548   case WM_PAINT: /* message: repaint portion of window */\r
4549     PaintProc(hwnd);\r
4550     break;\r
4551 \r
4552   case WM_ERASEBKGND:\r
4553     if (IsIconic(hwnd)) {\r
4554       /* Cheat; change the message */\r
4555       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4556     } else {\r
4557       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4558     }\r
4559     break;\r
4560 \r
4561   case WM_LBUTTONDOWN:\r
4562   case WM_MBUTTONDOWN:\r
4563   case WM_RBUTTONDOWN:\r
4564   case WM_LBUTTONUP:\r
4565   case WM_MBUTTONUP:\r
4566   case WM_RBUTTONUP:\r
4567   case WM_MOUSEMOVE:\r
4568   case WM_MOUSEWHEEL:\r
4569     MouseEvent(hwnd, message, wParam, lParam);\r
4570     break;\r
4571 \r
4572   JAWS_KB_NAVIGATION\r
4573 \r
4574   case WM_CHAR:\r
4575     \r
4576     JAWS_ALT_INTERCEPT\r
4577 \r
4578     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4579         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4580         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4581         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4582         SetFocus(h);\r
4583         SendMessage(h, message, wParam, lParam);\r
4584     } else if(lParam != KF_REPEAT) {\r
4585         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4586                 PopUpMoveDialog((char)wParam);\r
4587         } else if((char)wParam == 003) CopyGameToClipboard();\r
4588          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4589     }\r
4590 \r
4591     break;\r
4592 \r
4593   case WM_PALETTECHANGED:\r
4594     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4595       int nnew;\r
4596       HDC hdc = GetDC(hwndMain);\r
4597       SelectPalette(hdc, hPal, TRUE);\r
4598       nnew = RealizePalette(hdc);\r
4599       if (nnew > 0) {\r
4600         paletteChanged = TRUE;\r
4601         InvalidateRect(hwnd, &boardRect, FALSE);\r
4602       }\r
4603       ReleaseDC(hwnd, hdc);\r
4604     }\r
4605     break;\r
4606 \r
4607   case WM_QUERYNEWPALETTE:\r
4608     if (!appData.monoMode /*&& paletteChanged*/) {\r
4609       int nnew;\r
4610       HDC hdc = GetDC(hwndMain);\r
4611       paletteChanged = FALSE;\r
4612       SelectPalette(hdc, hPal, FALSE);\r
4613       nnew = RealizePalette(hdc);\r
4614       if (nnew > 0) {\r
4615         InvalidateRect(hwnd, &boardRect, FALSE);\r
4616       }\r
4617       ReleaseDC(hwnd, hdc);\r
4618       return TRUE;\r
4619     }\r
4620     return FALSE;\r
4621 \r
4622   case WM_COMMAND: /* message: command from application menu */\r
4623     wmId    = LOWORD(wParam);\r
4624     wmEvent = HIWORD(wParam);\r
4625 \r
4626     switch (wmId) {\r
4627     case IDM_NewGame:\r
4628       ResetGameEvent();\r
4629       SAY("new game enter a move to play against the computer with white");\r
4630       break;\r
4631 \r
4632     case IDM_NewGameFRC:\r
4633       if( NewGameFRC() == 0 ) {\r
4634         ResetGameEvent();\r
4635       }\r
4636       break;\r
4637 \r
4638     case IDM_NewVariant:\r
4639       NewVariantPopup(hwnd);\r
4640       break;\r
4641 \r
4642     case IDM_LoadGame:\r
4643       LoadGameDialog(hwnd, _("Load Game from File"));\r
4644       break;\r
4645 \r
4646     case IDM_LoadNextGame:\r
4647       ReloadGame(1);\r
4648       break;\r
4649 \r
4650     case IDM_LoadPrevGame:\r
4651       ReloadGame(-1);\r
4652       break;\r
4653 \r
4654     case IDM_ReloadGame:\r
4655       ReloadGame(0);\r
4656       break;\r
4657 \r
4658     case IDM_LoadPosition:\r
4659       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4660         Reset(FALSE, TRUE);\r
4661       }\r
4662       number = 1;\r
4663       f = OpenFileDialog(hwnd, "rb", "",\r
4664                          appData.oldSaveStyle ? "pos" : "fen",\r
4665                          POSITION_FILT,\r
4666                          _("Load Position from File"), &number, fileTitle, NULL);\r
4667       if (f != NULL) {\r
4668         LoadPosition(f, number, fileTitle);\r
4669       }\r
4670       break;\r
4671 \r
4672     case IDM_LoadNextPosition:\r
4673       ReloadPosition(1);\r
4674       break;\r
4675 \r
4676     case IDM_LoadPrevPosition:\r
4677       ReloadPosition(-1);\r
4678       break;\r
4679 \r
4680     case IDM_ReloadPosition:\r
4681       ReloadPosition(0);\r
4682       break;\r
4683 \r
4684     case IDM_SaveGame:\r
4685       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4686       f = OpenFileDialog(hwnd, "a", defName,\r
4687                          appData.oldSaveStyle ? "gam" : "pgn",\r
4688                          GAME_FILT,\r
4689                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4690       if (f != NULL) {\r
4691         SaveGame(f, 0, "");\r
4692       }\r
4693       break;\r
4694 \r
4695     case IDM_SavePosition:\r
4696       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4697       f = OpenFileDialog(hwnd, "a", defName,\r
4698                          appData.oldSaveStyle ? "pos" : "fen",\r
4699                          POSITION_FILT,\r
4700                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4701       if (f != NULL) {\r
4702         SavePosition(f, 0, "");\r
4703       }\r
4704       break;\r
4705 \r
4706     case IDM_SaveDiagram:\r
4707       defName = "diagram";\r
4708       f = OpenFileDialog(hwnd, "wb", defName,\r
4709                          "bmp",\r
4710                          DIAGRAM_FILT,\r
4711                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4712       if (f != NULL) {\r
4713         SaveDiagram(f);\r
4714       }\r
4715       break;\r
4716 \r
4717     case IDM_CopyGame:\r
4718       CopyGameToClipboard();\r
4719       break;\r
4720 \r
4721     case IDM_PasteGame:\r
4722       PasteGameFromClipboard();\r
4723       break;\r
4724 \r
4725     case IDM_CopyGameListToClipboard:\r
4726       CopyGameListToClipboard();\r
4727       break;\r
4728 \r
4729     /* [AS] Autodetect FEN or PGN data */\r
4730     case IDM_PasteAny:\r
4731       PasteGameOrFENFromClipboard();\r
4732       break;\r
4733 \r
4734     /* [AS] Move history */\r
4735     case IDM_ShowMoveHistory:\r
4736         if( MoveHistoryIsUp() ) {\r
4737             MoveHistoryPopDown();\r
4738         }\r
4739         else {\r
4740             MoveHistoryPopUp();\r
4741         }\r
4742         break;\r
4743 \r
4744     /* [AS] Eval graph */\r
4745     case IDM_ShowEvalGraph:\r
4746         if( EvalGraphIsUp() ) {\r
4747             EvalGraphPopDown();\r
4748         }\r
4749         else {\r
4750             EvalGraphPopUp();\r
4751             SetFocus(hwndMain);\r
4752         }\r
4753         break;\r
4754 \r
4755     /* [AS] Engine output */\r
4756     case IDM_ShowEngineOutput:\r
4757         if( EngineOutputIsUp() ) {\r
4758             EngineOutputPopDown();\r
4759         }\r
4760         else {\r
4761             EngineOutputPopUp();\r
4762         }\r
4763         break;\r
4764 \r
4765     /* [AS] User adjudication */\r
4766     case IDM_UserAdjudication_White:\r
4767         UserAdjudicationEvent( +1 );\r
4768         break;\r
4769 \r
4770     case IDM_UserAdjudication_Black:\r
4771         UserAdjudicationEvent( -1 );\r
4772         break;\r
4773 \r
4774     case IDM_UserAdjudication_Draw:\r
4775         UserAdjudicationEvent( 0 );\r
4776         break;\r
4777 \r
4778     /* [AS] Game list options dialog */\r
4779     case IDM_GameListOptions:\r
4780       GameListOptions();\r
4781       break;\r
4782 \r
4783     case IDM_NewChat:\r
4784       ChatPopUp(NULL);\r
4785       break;\r
4786 \r
4787     case IDM_CopyPosition:\r
4788       CopyFENToClipboard();\r
4789       break;\r
4790 \r
4791     case IDM_PastePosition:\r
4792       PasteFENFromClipboard();\r
4793       break;\r
4794 \r
4795     case IDM_MailMove:\r
4796       MailMoveEvent();\r
4797       break;\r
4798 \r
4799     case IDM_ReloadCMailMsg:\r
4800       Reset(TRUE, TRUE);\r
4801       ReloadCmailMsgEvent(FALSE);\r
4802       break;\r
4803 \r
4804     case IDM_Minimize:\r
4805       ShowWindow(hwnd, SW_MINIMIZE);\r
4806       break;\r
4807 \r
4808     case IDM_Exit:\r
4809       ExitEvent(0);\r
4810       break;\r
4811 \r
4812     case IDM_MachineWhite:\r
4813       MachineWhiteEvent();\r
4814       /*\r
4815        * refresh the tags dialog only if it's visible\r
4816        */\r
4817       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4818           char *tags;\r
4819           tags = PGNTags(&gameInfo);\r
4820           TagsPopUp(tags, CmailMsg());\r
4821           free(tags);\r
4822       }\r
4823       SAY("computer starts playing white");\r
4824       break;\r
4825 \r
4826     case IDM_MachineBlack:\r
4827       MachineBlackEvent();\r
4828       /*\r
4829        * refresh the tags dialog only if it's visible\r
4830        */\r
4831       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4832           char *tags;\r
4833           tags = PGNTags(&gameInfo);\r
4834           TagsPopUp(tags, CmailMsg());\r
4835           free(tags);\r
4836       }\r
4837       SAY("computer starts playing black");\r
4838       break;\r
4839 \r
4840     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4841       if(gameMode != BeginningOfGame) { // allow menu item to remain enabled for better mode highligting\r
4842         DisplayError(_("You can only start a match from the initial position."), 0); break;\r
4843       }\r
4844       matchMode = 2;// distinguish from command-line-triggered case (matchMode=1)\r
4845       appData.matchGames = appData.defaultMatchGames;\r
4846       matchGame = 1;\r
4847       first.matchWins = second.matchWins = 0;\r
4848 \r
4849     case IDM_TwoMachines:\r
4850       TwoMachinesEvent();\r
4851       /*\r
4852        * refresh the tags dialog only if it's visible\r
4853        */\r
4854       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4855           char *tags;\r
4856           tags = PGNTags(&gameInfo);\r
4857           TagsPopUp(tags, CmailMsg());\r
4858           free(tags);\r
4859       }\r
4860       SAY("computer starts playing both sides");\r
4861       break;\r
4862 \r
4863     case IDM_AnalysisMode:\r
4864       if (!first.analysisSupport) {\r
4865         snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4866         DisplayError(buf, 0);\r
4867       } else {\r
4868         SAY("analyzing current position");\r
4869         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4870         if (appData.icsActive) {\r
4871                if (gameMode != IcsObserving) {\r
4872                  snprintf(buf, MSG_SIZ, "You are not observing a game");\r
4873                        DisplayError(buf, 0);\r
4874                        /* secure check */\r
4875                        if (appData.icsEngineAnalyze) {\r
4876                                if (appData.debugMode) \r
4877                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4878                                ExitAnalyzeMode();\r
4879                                ModeHighlight();\r
4880                                break;\r
4881                        }\r
4882                        break;\r
4883                } else {\r
4884                        /* if enable, user want disable icsEngineAnalyze */\r
4885                        if (appData.icsEngineAnalyze) {\r
4886                                ExitAnalyzeMode();\r
4887                                ModeHighlight();\r
4888                                break;\r
4889                        }\r
4890                        appData.icsEngineAnalyze = TRUE;\r
4891                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4892                }\r
4893         } \r
4894         if (!appData.showThinking) ToggleShowThinking();\r
4895         AnalyzeModeEvent();\r
4896       }\r
4897       break;\r
4898 \r
4899     case IDM_AnalyzeFile:\r
4900       if (!first.analysisSupport) {\r
4901         char buf[MSG_SIZ];\r
4902           snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4903         DisplayError(buf, 0);\r
4904       } else {\r
4905         if (!appData.showThinking) ToggleShowThinking();\r
4906         AnalyzeFileEvent();\r
4907         LoadGameDialog(hwnd, _("Analyze Game from File"));\r
4908         AnalysisPeriodicEvent(1);\r
4909       }\r
4910       break;\r
4911 \r
4912     case IDM_IcsClient:\r
4913       IcsClientEvent();\r
4914       break;\r
4915 \r
4916     case IDM_EditGame:\r
4917     case IDM_EditGame2:\r
4918       EditGameEvent();\r
4919       SAY("edit game");\r
4920       break;\r
4921 \r
4922     case IDM_EditPosition:\r
4923     case IDM_EditPosition2:\r
4924       EditPositionEvent();\r
4925       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4926       break;\r
4927 \r
4928     case IDM_Training:\r
4929       TrainingEvent();\r
4930       break;\r
4931 \r
4932     case IDM_ShowGameList:\r
4933       ShowGameListProc();\r
4934       break;\r
4935 \r
4936     case IDM_EditProgs1:\r
4937       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
4938       break;\r
4939 \r
4940     case IDM_EditProgs2:\r
4941       EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);\r
4942       break;\r
4943 \r
4944     case IDM_EditServers:\r
4945       EditTagsPopUp(icsNames, &icsNames);\r
4946       break;\r
4947 \r
4948     case IDM_EditTags:\r
4949     case IDM_Tags:\r
4950       EditTagsProc();\r
4951       break;\r
4952 \r
4953     case IDM_EditComment:\r
4954     case IDM_Comment:\r
4955       if (commentUp && editComment) {\r
4956         CommentPopDown();\r
4957       } else {\r
4958         EditCommentEvent();\r
4959       }\r
4960       break;\r
4961 \r
4962     case IDM_Pause:\r
4963       PauseEvent();\r
4964       break;\r
4965 \r
4966     case IDM_Accept:\r
4967       AcceptEvent();\r
4968       break;\r
4969 \r
4970     case IDM_Decline:\r
4971       DeclineEvent();\r
4972       break;\r
4973 \r
4974     case IDM_Rematch:\r
4975       RematchEvent();\r
4976       break;\r
4977 \r
4978     case IDM_CallFlag:\r
4979       CallFlagEvent();\r
4980       break;\r
4981 \r
4982     case IDM_Draw:\r
4983       DrawEvent();\r
4984       break;\r
4985 \r
4986     case IDM_Adjourn:\r
4987       AdjournEvent();\r
4988       break;\r
4989 \r
4990     case IDM_Abort:\r
4991       AbortEvent();\r
4992       break;\r
4993 \r
4994     case IDM_Resign:\r
4995       ResignEvent();\r
4996       break;\r
4997 \r
4998     case IDM_StopObserving:\r
4999       StopObservingEvent();\r
5000       break;\r
5001 \r
5002     case IDM_StopExamining:\r
5003       StopExaminingEvent();\r
5004       break;\r
5005 \r
5006     case IDM_Upload:\r
5007       UploadGameEvent();\r
5008       break;\r
5009 \r
5010     case IDM_TypeInMove:\r
5011       PopUpMoveDialog('\000');\r
5012       break;\r
5013 \r
5014     case IDM_TypeInName:\r
5015       PopUpNameDialog('\000');\r
5016       break;\r
5017 \r
5018     case IDM_Backward:\r
5019       BackwardEvent();\r
5020       SetFocus(hwndMain);\r
5021       break;\r
5022 \r
5023     JAWS_MENU_ITEMS\r
5024 \r
5025     case IDM_Forward:\r
5026       ForwardEvent();\r
5027       SetFocus(hwndMain);\r
5028       break;\r
5029 \r
5030     case IDM_ToStart:\r
5031       ToStartEvent();\r
5032       SetFocus(hwndMain);\r
5033       break;\r
5034 \r
5035     case IDM_ToEnd:\r
5036       ToEndEvent();\r
5037       SetFocus(hwndMain);\r
5038       break;\r
5039 \r
5040     case IDM_Revert:\r
5041       RevertEvent(FALSE);\r
5042       break;\r
5043 \r
5044     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5045       RevertEvent(TRUE);\r
5046       break;\r
5047 \r
5048     case IDM_TruncateGame:\r
5049       TruncateGameEvent();\r
5050       break;\r
5051 \r
5052     case IDM_MoveNow:\r
5053       MoveNowEvent();\r
5054       break;\r
5055 \r
5056     case IDM_RetractMove:\r
5057       RetractMoveEvent();\r
5058       break;\r
5059 \r
5060     case IDM_FlipView:\r
5061       flipView = !flipView;\r
5062       DrawPosition(FALSE, NULL);\r
5063       break;\r
5064 \r
5065     case IDM_FlipClock:\r
5066       flipClock = !flipClock;\r
5067       DisplayBothClocks();\r
5068       DisplayLogos();\r
5069       break;\r
5070 \r
5071     case IDM_MuteSounds:\r
5072       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5073       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5074                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5075       break;\r
5076 \r
5077     case IDM_GeneralOptions:\r
5078       GeneralOptionsPopup(hwnd);\r
5079       DrawPosition(TRUE, NULL);\r
5080       break;\r
5081 \r
5082     case IDM_BoardOptions:\r
5083       BoardOptionsPopup(hwnd);\r
5084       break;\r
5085 \r
5086     case IDM_EnginePlayOptions:\r
5087       EnginePlayOptionsPopup(hwnd);\r
5088       break;\r
5089 \r
5090     case IDM_Engine1Options:\r
5091       EngineOptionsPopup(hwnd, &first);\r
5092       break;\r
5093 \r
5094     case IDM_Engine2Options:\r
5095       savedHwnd = hwnd;\r
5096       if(WaitForSecond(SettingsMenuIfReady)) break;\r
5097       EngineOptionsPopup(hwnd, &second);\r
5098       break;\r
5099 \r
5100     case IDM_OptionsUCI:\r
5101       UciOptionsPopup(hwnd);\r
5102       break;\r
5103 \r
5104     case IDM_IcsOptions:\r
5105       IcsOptionsPopup(hwnd);\r
5106       break;\r
5107 \r
5108     case IDM_Fonts:\r
5109       FontsOptionsPopup(hwnd);\r
5110       break;\r
5111 \r
5112     case IDM_Sounds:\r
5113       SoundOptionsPopup(hwnd);\r
5114       break;\r
5115 \r
5116     case IDM_CommPort:\r
5117       CommPortOptionsPopup(hwnd);\r
5118       break;\r
5119 \r
5120     case IDM_LoadOptions:\r
5121       LoadOptionsPopup(hwnd);\r
5122       break;\r
5123 \r
5124     case IDM_SaveOptions:\r
5125       SaveOptionsPopup(hwnd);\r
5126       break;\r
5127 \r
5128     case IDM_TimeControl:\r
5129       TimeControlOptionsPopup(hwnd);\r
5130       break;\r
5131 \r
5132     case IDM_SaveSettings:\r
5133       SaveSettings(settingsFileName);\r
5134       break;\r
5135 \r
5136     case IDM_SaveSettingsOnExit:\r
5137       saveSettingsOnExit = !saveSettingsOnExit;\r
5138       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5139                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5140                                          MF_CHECKED : MF_UNCHECKED));\r
5141       break;\r
5142 \r
5143     case IDM_Hint:\r
5144       HintEvent();\r
5145       break;\r
5146 \r
5147     case IDM_Book:\r
5148       BookEvent();\r
5149       break;\r
5150 \r
5151     case IDM_AboutGame:\r
5152       AboutGameEvent();\r
5153       break;\r
5154 \r
5155     case IDM_Debug:\r
5156       appData.debugMode = !appData.debugMode;\r
5157       if (appData.debugMode) {\r
5158         char dir[MSG_SIZ];\r
5159         GetCurrentDirectory(MSG_SIZ, dir);\r
5160         SetCurrentDirectory(installDir);\r
5161         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5162         SetCurrentDirectory(dir);\r
5163         setbuf(debugFP, NULL);\r
5164       } else {\r
5165         fclose(debugFP);\r
5166         debugFP = NULL;\r
5167       }\r
5168       break;\r
5169 \r
5170     case IDM_HELPCONTENTS:\r
5171       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5172           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5173           MessageBox (GetFocus(),\r
5174                     _("Unable to activate help"),\r
5175                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5176       }\r
5177       break;\r
5178 \r
5179     case IDM_HELPSEARCH:\r
5180         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5181             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5182         MessageBox (GetFocus(),\r
5183                     _("Unable to activate help"),\r
5184                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5185       }\r
5186       break;\r
5187 \r
5188     case IDM_HELPHELP:\r
5189       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5190         MessageBox (GetFocus(),\r
5191                     _("Unable to activate help"),\r
5192                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5193       }\r
5194       break;\r
5195 \r
5196     case IDM_ABOUT:\r
5197       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5198       DialogBox(hInst, \r
5199         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5200         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5201       FreeProcInstance(lpProc);\r
5202       break;\r
5203 \r
5204     case IDM_DirectCommand1:\r
5205       AskQuestionEvent(_("Direct Command"),\r
5206                        _("Send to chess program:"), "", "1");\r
5207       break;\r
5208     case IDM_DirectCommand2:\r
5209       AskQuestionEvent(_("Direct Command"),\r
5210                        _("Send to second chess program:"), "", "2");\r
5211       break;\r
5212 \r
5213     case EP_WhitePawn:\r
5214       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5215       fromX = fromY = -1;\r
5216       break;\r
5217 \r
5218     case EP_WhiteKnight:\r
5219       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5220       fromX = fromY = -1;\r
5221       break;\r
5222 \r
5223     case EP_WhiteBishop:\r
5224       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5225       fromX = fromY = -1;\r
5226       break;\r
5227 \r
5228     case EP_WhiteRook:\r
5229       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5230       fromX = fromY = -1;\r
5231       break;\r
5232 \r
5233     case EP_WhiteQueen:\r
5234       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5235       fromX = fromY = -1;\r
5236       break;\r
5237 \r
5238     case EP_WhiteFerz:\r
5239       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5240       fromX = fromY = -1;\r
5241       break;\r
5242 \r
5243     case EP_WhiteWazir:\r
5244       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5245       fromX = fromY = -1;\r
5246       break;\r
5247 \r
5248     case EP_WhiteAlfil:\r
5249       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5250       fromX = fromY = -1;\r
5251       break;\r
5252 \r
5253     case EP_WhiteCannon:\r
5254       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5255       fromX = fromY = -1;\r
5256       break;\r
5257 \r
5258     case EP_WhiteCardinal:\r
5259       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5260       fromX = fromY = -1;\r
5261       break;\r
5262 \r
5263     case EP_WhiteMarshall:\r
5264       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5265       fromX = fromY = -1;\r
5266       break;\r
5267 \r
5268     case EP_WhiteKing:\r
5269       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5270       fromX = fromY = -1;\r
5271       break;\r
5272 \r
5273     case EP_BlackPawn:\r
5274       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5275       fromX = fromY = -1;\r
5276       break;\r
5277 \r
5278     case EP_BlackKnight:\r
5279       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5280       fromX = fromY = -1;\r
5281       break;\r
5282 \r
5283     case EP_BlackBishop:\r
5284       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5285       fromX = fromY = -1;\r
5286       break;\r
5287 \r
5288     case EP_BlackRook:\r
5289       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5290       fromX = fromY = -1;\r
5291       break;\r
5292 \r
5293     case EP_BlackQueen:\r
5294       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5295       fromX = fromY = -1;\r
5296       break;\r
5297 \r
5298     case EP_BlackFerz:\r
5299       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5300       fromX = fromY = -1;\r
5301       break;\r
5302 \r
5303     case EP_BlackWazir:\r
5304       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5305       fromX = fromY = -1;\r
5306       break;\r
5307 \r
5308     case EP_BlackAlfil:\r
5309       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5310       fromX = fromY = -1;\r
5311       break;\r
5312 \r
5313     case EP_BlackCannon:\r
5314       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5315       fromX = fromY = -1;\r
5316       break;\r
5317 \r
5318     case EP_BlackCardinal:\r
5319       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5320       fromX = fromY = -1;\r
5321       break;\r
5322 \r
5323     case EP_BlackMarshall:\r
5324       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5325       fromX = fromY = -1;\r
5326       break;\r
5327 \r
5328     case EP_BlackKing:\r
5329       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5330       fromX = fromY = -1;\r
5331       break;\r
5332 \r
5333     case EP_EmptySquare:\r
5334       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5335       fromX = fromY = -1;\r
5336       break;\r
5337 \r
5338     case EP_ClearBoard:\r
5339       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5340       fromX = fromY = -1;\r
5341       break;\r
5342 \r
5343     case EP_White:\r
5344       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5345       fromX = fromY = -1;\r
5346       break;\r
5347 \r
5348     case EP_Black:\r
5349       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5350       fromX = fromY = -1;\r
5351       break;\r
5352 \r
5353     case EP_Promote:\r
5354       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5355       fromX = fromY = -1;\r
5356       break;\r
5357 \r
5358     case EP_Demote:\r
5359       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5360       fromX = fromY = -1;\r
5361       break;\r
5362 \r
5363     case DP_Pawn:\r
5364       DropMenuEvent(WhitePawn, fromX, fromY);\r
5365       fromX = fromY = -1;\r
5366       break;\r
5367 \r
5368     case DP_Knight:\r
5369       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5370       fromX = fromY = -1;\r
5371       break;\r
5372 \r
5373     case DP_Bishop:\r
5374       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5375       fromX = fromY = -1;\r
5376       break;\r
5377 \r
5378     case DP_Rook:\r
5379       DropMenuEvent(WhiteRook, fromX, fromY);\r
5380       fromX = fromY = -1;\r
5381       break;\r
5382 \r
5383     case DP_Queen:\r
5384       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5385       fromX = fromY = -1;\r
5386       break;\r
5387 \r
5388     case IDM_English:\r
5389       barbaric = 0; appData.language = "";\r
5390       TranslateMenus(0);\r
5391       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5392       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5393       lastChecked = wmId;\r
5394       break;\r
5395 \r
5396     default:\r
5397       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5398           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5399           TranslateMenus(0);\r
5400           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5401           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5402           lastChecked = wmId;\r
5403           break;\r
5404       }\r
5405       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5406     }\r
5407     break;\r
5408 \r
5409   case WM_TIMER:\r
5410     switch (wParam) {\r
5411     case CLOCK_TIMER_ID:\r
5412       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5413       clockTimerEvent = 0;\r
5414       DecrementClocks(); /* call into back end */\r
5415       break;\r
5416     case LOAD_GAME_TIMER_ID:\r
5417       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5418       loadGameTimerEvent = 0;\r
5419       AutoPlayGameLoop(); /* call into back end */\r
5420       break;\r
5421     case ANALYSIS_TIMER_ID:\r
5422       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5423                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5424         AnalysisPeriodicEvent(0);\r
5425       } else {\r
5426         KillTimer(hwnd, analysisTimerEvent);\r
5427         analysisTimerEvent = 0;\r
5428       }\r
5429       break;\r
5430     case DELAYED_TIMER_ID:\r
5431       KillTimer(hwnd, delayedTimerEvent);\r
5432       delayedTimerEvent = 0;\r
5433       delayedTimerCallback();\r
5434       break;\r
5435     }\r
5436     break;\r
5437 \r
5438   case WM_USER_Input:\r
5439     InputEvent(hwnd, message, wParam, lParam);\r
5440     break;\r
5441 \r
5442   /* [AS] Also move "attached" child windows */\r
5443   case WM_WINDOWPOSCHANGING:\r
5444 \r
5445     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5446         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5447 \r
5448         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5449             /* Window is moving */\r
5450             RECT rcMain;\r
5451 \r
5452 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5453             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5454             rcMain.right  = wpMain.x + wpMain.width;\r
5455             rcMain.top    = wpMain.y;\r
5456             rcMain.bottom = wpMain.y + wpMain.height;\r
5457             \r
5458             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5459             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5460             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5461             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5462             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5463             wpMain.x = lpwp->x;\r
5464             wpMain.y = lpwp->y;\r
5465         }\r
5466     }\r
5467     break;\r
5468 \r
5469   /* [AS] Snapping */\r
5470   case WM_ENTERSIZEMOVE:\r
5471     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5472     if (hwnd == hwndMain) {\r
5473       doingSizing = TRUE;\r
5474       lastSizing = 0;\r
5475     }\r
5476     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5477     break;\r
5478 \r
5479   case WM_SIZING:\r
5480     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5481     if (hwnd == hwndMain) {\r
5482       lastSizing = wParam;\r
5483     }\r
5484     break;\r
5485 \r
5486   case WM_MOVING:\r
5487     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5488       return OnMoving( &sd, hwnd, wParam, lParam );\r
5489 \r
5490   case WM_EXITSIZEMOVE:\r
5491     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5492     if (hwnd == hwndMain) {\r
5493       RECT client;\r
5494       doingSizing = FALSE;\r
5495       InvalidateRect(hwnd, &boardRect, FALSE);\r
5496       GetClientRect(hwnd, &client);\r
5497       ResizeBoard(client.right, client.bottom, lastSizing);\r
5498       lastSizing = 0;\r
5499       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5500     }\r
5501     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5502     break;\r
5503 \r
5504   case WM_DESTROY: /* message: window being destroyed */\r
5505     PostQuitMessage(0);\r
5506     break;\r
5507 \r
5508   case WM_CLOSE:\r
5509     if (hwnd == hwndMain) {\r
5510       ExitEvent(0);\r
5511     }\r
5512     break;\r
5513 \r
5514   default:      /* Passes it on if unprocessed */\r
5515     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5516   }\r
5517   return 0;\r
5518 }\r
5519 \r
5520 /*---------------------------------------------------------------------------*\\r
5521  *\r
5522  * Misc utility routines\r
5523  *\r
5524 \*---------------------------------------------------------------------------*/\r
5525 \r
5526 /*\r
5527  * Decent random number generator, at least not as bad as Windows\r
5528  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5529  */\r
5530 unsigned int randstate;\r
5531 \r
5532 int\r
5533 myrandom(void)\r
5534 {\r
5535   randstate = randstate * 1664525 + 1013904223;\r
5536   return (int) randstate & 0x7fffffff;\r
5537 }\r
5538 \r
5539 void\r
5540 mysrandom(unsigned int seed)\r
5541 {\r
5542   randstate = seed;\r
5543 }\r
5544 \r
5545 \r
5546 /* \r
5547  * returns TRUE if user selects a different color, FALSE otherwise \r
5548  */\r
5549 \r
5550 BOOL\r
5551 ChangeColor(HWND hwnd, COLORREF *which)\r
5552 {\r
5553   static BOOL firstTime = TRUE;\r
5554   static DWORD customColors[16];\r
5555   CHOOSECOLOR cc;\r
5556   COLORREF newcolor;\r
5557   int i;\r
5558   ColorClass ccl;\r
5559 \r
5560   if (firstTime) {\r
5561     /* Make initial colors in use available as custom colors */\r
5562     /* Should we put the compiled-in defaults here instead? */\r
5563     i = 0;\r
5564     customColors[i++] = lightSquareColor & 0xffffff;\r
5565     customColors[i++] = darkSquareColor & 0xffffff;\r
5566     customColors[i++] = whitePieceColor & 0xffffff;\r
5567     customColors[i++] = blackPieceColor & 0xffffff;\r
5568     customColors[i++] = highlightSquareColor & 0xffffff;\r
5569     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5570 \r
5571     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5572       customColors[i++] = textAttribs[ccl].color;\r
5573     }\r
5574     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5575     firstTime = FALSE;\r
5576   }\r
5577 \r
5578   cc.lStructSize = sizeof(cc);\r
5579   cc.hwndOwner = hwnd;\r
5580   cc.hInstance = NULL;\r
5581   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5582   cc.lpCustColors = (LPDWORD) customColors;\r
5583   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5584 \r
5585   if (!ChooseColor(&cc)) return FALSE;\r
5586 \r
5587   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5588   if (newcolor == *which) return FALSE;\r
5589   *which = newcolor;\r
5590   return TRUE;\r
5591 \r
5592   /*\r
5593   InitDrawingColors();\r
5594   InvalidateRect(hwnd, &boardRect, FALSE);\r
5595   */\r
5596 }\r
5597 \r
5598 BOOLEAN\r
5599 MyLoadSound(MySound *ms)\r
5600 {\r
5601   BOOL ok = FALSE;\r
5602   struct stat st;\r
5603   FILE *f;\r
5604 \r
5605   if (ms->data) free(ms->data);\r
5606   ms->data = NULL;\r
5607 \r
5608   switch (ms->name[0]) {\r
5609   case NULLCHAR:\r
5610     /* Silence */\r
5611     ok = TRUE;\r
5612     break;\r
5613   case '$':\r
5614     /* System sound from Control Panel.  Don't preload here. */\r
5615     ok = TRUE;\r
5616     break;\r
5617   case '!':\r
5618     if (ms->name[1] == NULLCHAR) {\r
5619       /* "!" alone = silence */\r
5620       ok = TRUE;\r
5621     } else {\r
5622       /* Builtin wave resource.  Error if not found. */\r
5623       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5624       if (h == NULL) break;\r
5625       ms->data = (void *)LoadResource(hInst, h);\r
5626       if (h == NULL) break;\r
5627       ok = TRUE;\r
5628     }\r
5629     break;\r
5630   default:\r
5631     /* .wav file.  Error if not found. */\r
5632     f = fopen(ms->name, "rb");\r
5633     if (f == NULL) break;\r
5634     if (fstat(fileno(f), &st) < 0) break;\r
5635     ms->data = malloc(st.st_size);\r
5636     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5637     fclose(f);\r
5638     ok = TRUE;\r
5639     break;\r
5640   }\r
5641   if (!ok) {\r
5642     char buf[MSG_SIZ];\r
5643       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5644     DisplayError(buf, GetLastError());\r
5645   }\r
5646   return ok;\r
5647 }\r
5648 \r
5649 BOOLEAN\r
5650 MyPlaySound(MySound *ms)\r
5651 {\r
5652   BOOLEAN ok = FALSE;\r
5653 \r
5654   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5655   switch (ms->name[0]) {\r
5656   case NULLCHAR:\r
5657         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5658     /* Silence */\r
5659     ok = TRUE;\r
5660     break;\r
5661   case '$':\r
5662     /* System sound from Control Panel (deprecated feature).\r
5663        "$" alone or an unset sound name gets default beep (still in use). */\r
5664     if (ms->name[1]) {\r
5665       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5666     }\r
5667     if (!ok) ok = MessageBeep(MB_OK);\r
5668     break; \r
5669   case '!':\r
5670     /* Builtin wave resource, or "!" alone for silence */\r
5671     if (ms->name[1]) {\r
5672       if (ms->data == NULL) return FALSE;\r
5673       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5674     } else {\r
5675       ok = TRUE;\r
5676     }\r
5677     break;\r
5678   default:\r
5679     /* .wav file.  Error if not found. */\r
5680     if (ms->data == NULL) return FALSE;\r
5681     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5682     break;\r
5683   }\r
5684   /* Don't print an error: this can happen innocently if the sound driver\r
5685      is busy; for instance, if another instance of WinBoard is playing\r
5686      a sound at about the same time. */\r
5687   return ok;\r
5688 }\r
5689 \r
5690 \r
5691 LRESULT CALLBACK\r
5692 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5693 {\r
5694   BOOL ok;\r
5695   OPENFILENAME *ofn;\r
5696   static UINT *number; /* gross that this is static */\r
5697 \r
5698   switch (message) {\r
5699   case WM_INITDIALOG: /* message: initialize dialog box */\r
5700     /* Center the dialog over the application window */\r
5701     ofn = (OPENFILENAME *) lParam;\r
5702     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5703       number = (UINT *) ofn->lCustData;\r
5704       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5705     } else {\r
5706       number = NULL;\r
5707     }\r
5708     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5709     Translate(hDlg, 1536);\r
5710     return FALSE;  /* Allow for further processing */\r
5711 \r
5712   case WM_COMMAND:\r
5713     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5714       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5715     }\r
5716     return FALSE;  /* Allow for further processing */\r
5717   }\r
5718   return FALSE;\r
5719 }\r
5720 \r
5721 UINT APIENTRY\r
5722 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5723 {\r
5724   static UINT *number;\r
5725   OPENFILENAME *ofname;\r
5726   OFNOTIFY *ofnot;\r
5727   switch (uiMsg) {\r
5728   case WM_INITDIALOG:\r
5729     Translate(hdlg, DLG_IndexNumber);\r
5730     ofname = (OPENFILENAME *)lParam;\r
5731     number = (UINT *)(ofname->lCustData);\r
5732     break;\r
5733   case WM_NOTIFY:\r
5734     ofnot = (OFNOTIFY *)lParam;\r
5735     if (ofnot->hdr.code == CDN_FILEOK) {\r
5736       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5737     }\r
5738     break;\r
5739   }\r
5740   return 0;\r
5741 }\r
5742 \r
5743 \r
5744 FILE *\r
5745 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5746                char *nameFilt, char *dlgTitle, UINT *number,\r
5747                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5748 {\r
5749   OPENFILENAME openFileName;\r
5750   char buf1[MSG_SIZ];\r
5751   FILE *f;\r
5752 \r
5753   if (fileName == NULL) fileName = buf1;\r
5754   if (defName == NULL) {\r
5755     safeStrCpy(fileName, "*.", 3 );\r
5756     strcat(fileName, defExt);\r
5757   } else {\r
5758     safeStrCpy(fileName, defName, MSG_SIZ );\r
5759   }\r
5760     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5761   if (number) *number = 0;\r
5762 \r
5763   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5764   openFileName.hwndOwner         = hwnd;\r
5765   openFileName.hInstance         = (HANDLE) hInst;\r
5766   openFileName.lpstrFilter       = nameFilt;\r
5767   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5768   openFileName.nMaxCustFilter    = 0L;\r
5769   openFileName.nFilterIndex      = 1L;\r
5770   openFileName.lpstrFile         = fileName;\r
5771   openFileName.nMaxFile          = MSG_SIZ;\r
5772   openFileName.lpstrFileTitle    = fileTitle;\r
5773   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5774   openFileName.lpstrInitialDir   = NULL;\r
5775   openFileName.lpstrTitle        = dlgTitle;\r
5776   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5777     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5778     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5779     | (oldDialog ? 0 : OFN_EXPLORER);\r
5780   openFileName.nFileOffset       = 0;\r
5781   openFileName.nFileExtension    = 0;\r
5782   openFileName.lpstrDefExt       = defExt;\r
5783   openFileName.lCustData         = (LONG) number;\r
5784   openFileName.lpfnHook          = oldDialog ?\r
5785     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5786   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5787 \r
5788   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5789                         GetOpenFileName(&openFileName)) {\r
5790     /* open the file */\r
5791     f = fopen(openFileName.lpstrFile, write);\r
5792     if (f == NULL) {\r
5793       MessageBox(hwnd, _("File open failed"), NULL,\r
5794                  MB_OK|MB_ICONEXCLAMATION);\r
5795       return NULL;\r
5796     }\r
5797   } else {\r
5798     int err = CommDlgExtendedError();\r
5799     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5800     return FALSE;\r
5801   }\r
5802   return f;\r
5803 }\r
5804 \r
5805 \r
5806 \r
5807 VOID APIENTRY\r
5808 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5809 {\r
5810   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5811 \r
5812   /*\r
5813    * Get the first pop-up menu in the menu template. This is the\r
5814    * menu that TrackPopupMenu displays.\r
5815    */\r
5816   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5817   TranslateOneMenu(10, hmenuTrackPopup);\r
5818 \r
5819   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5820 \r
5821   /*\r
5822    * TrackPopup uses screen coordinates, so convert the\r
5823    * coordinates of the mouse click to screen coordinates.\r
5824    */\r
5825   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5826 \r
5827   /* Draw and track the floating pop-up menu. */\r
5828   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5829                  pt.x, pt.y, 0, hwnd, NULL);\r
5830 \r
5831   /* Destroy the menu.*/\r
5832   DestroyMenu(hmenu);\r
5833 }\r
5834    \r
5835 typedef struct {\r
5836   HWND hDlg, hText;\r
5837   int sizeX, sizeY, newSizeX, newSizeY;\r
5838   HDWP hdwp;\r
5839 } ResizeEditPlusButtonsClosure;\r
5840 \r
5841 BOOL CALLBACK\r
5842 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5843 {\r
5844   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5845   RECT rect;\r
5846   POINT pt;\r
5847 \r
5848   if (hChild == cl->hText) return TRUE;\r
5849   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5850   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5851   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5852   ScreenToClient(cl->hDlg, &pt);\r
5853   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5854     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5855   return TRUE;\r
5856 }\r
5857 \r
5858 /* Resize a dialog that has a (rich) edit field filling most of\r
5859    the top, with a row of buttons below */\r
5860 VOID\r
5861 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5862 {\r
5863   RECT rectText;\r
5864   int newTextHeight, newTextWidth;\r
5865   ResizeEditPlusButtonsClosure cl;\r
5866   \r
5867   /*if (IsIconic(hDlg)) return;*/\r
5868   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5869   \r
5870   cl.hdwp = BeginDeferWindowPos(8);\r
5871 \r
5872   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5873   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5874   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5875   if (newTextHeight < 0) {\r
5876     newSizeY += -newTextHeight;\r
5877     newTextHeight = 0;\r
5878   }\r
5879   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5880     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5881 \r
5882   cl.hDlg = hDlg;\r
5883   cl.hText = hText;\r
5884   cl.sizeX = sizeX;\r
5885   cl.sizeY = sizeY;\r
5886   cl.newSizeX = newSizeX;\r
5887   cl.newSizeY = newSizeY;\r
5888   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5889 \r
5890   EndDeferWindowPos(cl.hdwp);\r
5891 }\r
5892 \r
5893 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5894 {\r
5895     RECT    rChild, rParent;\r
5896     int     wChild, hChild, wParent, hParent;\r
5897     int     wScreen, hScreen, xNew, yNew;\r
5898     HDC     hdc;\r
5899 \r
5900     /* Get the Height and Width of the child window */\r
5901     GetWindowRect (hwndChild, &rChild);\r
5902     wChild = rChild.right - rChild.left;\r
5903     hChild = rChild.bottom - rChild.top;\r
5904 \r
5905     /* Get the Height and Width of the parent window */\r
5906     GetWindowRect (hwndParent, &rParent);\r
5907     wParent = rParent.right - rParent.left;\r
5908     hParent = rParent.bottom - rParent.top;\r
5909 \r
5910     /* Get the display limits */\r
5911     hdc = GetDC (hwndChild);\r
5912     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5913     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5914     ReleaseDC(hwndChild, hdc);\r
5915 \r
5916     /* Calculate new X position, then adjust for screen */\r
5917     xNew = rParent.left + ((wParent - wChild) /2);\r
5918     if (xNew < 0) {\r
5919         xNew = 0;\r
5920     } else if ((xNew+wChild) > wScreen) {\r
5921         xNew = wScreen - wChild;\r
5922     }\r
5923 \r
5924     /* Calculate new Y position, then adjust for screen */\r
5925     if( mode == 0 ) {\r
5926         yNew = rParent.top  + ((hParent - hChild) /2);\r
5927     }\r
5928     else {\r
5929         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5930     }\r
5931 \r
5932     if (yNew < 0) {\r
5933         yNew = 0;\r
5934     } else if ((yNew+hChild) > hScreen) {\r
5935         yNew = hScreen - hChild;\r
5936     }\r
5937 \r
5938     /* Set it, and return */\r
5939     return SetWindowPos (hwndChild, NULL,\r
5940                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5941 }\r
5942 \r
5943 /* Center one window over another */\r
5944 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
5945 {\r
5946     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
5947 }\r
5948 \r
5949 /*---------------------------------------------------------------------------*\\r
5950  *\r
5951  * Startup Dialog functions\r
5952  *\r
5953 \*---------------------------------------------------------------------------*/\r
5954 void\r
5955 InitComboStrings(HANDLE hwndCombo, char **cd)\r
5956 {\r
5957   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5958 \r
5959   while (*cd != NULL) {\r
5960     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
5961     cd++;\r
5962   }\r
5963 }\r
5964 \r
5965 void\r
5966 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
5967 {\r
5968   char buf1[MAX_ARG_LEN];\r
5969   int len;\r
5970 \r
5971   if (str[0] == '@') {\r
5972     FILE* f = fopen(str + 1, "r");\r
5973     if (f == NULL) {\r
5974       DisplayFatalError(str + 1, errno, 2);\r
5975       return;\r
5976     }\r
5977     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
5978     fclose(f);\r
5979     buf1[len] = NULLCHAR;\r
5980     str = buf1;\r
5981   }\r
5982 \r
5983   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5984 \r
5985   for (;;) {\r
5986     char buf[MSG_SIZ];\r
5987     char *end = strchr(str, '\n');\r
5988     if (end == NULL) return;\r
5989     memcpy(buf, str, end - str);\r
5990     buf[end - str] = NULLCHAR;\r
5991     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
5992     str = end + 1;\r
5993   }\r
5994 }\r
5995 \r
5996 void\r
5997 SetStartupDialogEnables(HWND hDlg)\r
5998 {\r
5999   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6000     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6001     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6002   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6003     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6004   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6005     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6006   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6007     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6008   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6009     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6010     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6011     IsDlgButtonChecked(hDlg, OPT_View));\r
6012 }\r
6013 \r
6014 char *\r
6015 QuoteForFilename(char *filename)\r
6016 {\r
6017   int dquote, space;\r
6018   dquote = strchr(filename, '"') != NULL;\r
6019   space = strchr(filename, ' ') != NULL;\r
6020   if (dquote || space) {\r
6021     if (dquote) {\r
6022       return "'";\r
6023     } else {\r
6024       return "\"";\r
6025     }\r
6026   } else {\r
6027     return "";\r
6028   }\r
6029 }\r
6030 \r
6031 VOID\r
6032 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6033 {\r
6034   char buf[MSG_SIZ];\r
6035   char *q;\r
6036 \r
6037   InitComboStringsFromOption(hwndCombo, nthnames);\r
6038   q = QuoteForFilename(nthcp);\r
6039     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6040   if (*nthdir != NULLCHAR) {\r
6041     q = QuoteForFilename(nthdir);\r
6042       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6043   }\r
6044   if (*nthcp == NULLCHAR) {\r
6045     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6046   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6047     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6048     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6049   }\r
6050 }\r
6051 \r
6052 LRESULT CALLBACK\r
6053 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6054 {\r
6055   char buf[MSG_SIZ];\r
6056   HANDLE hwndCombo;\r
6057   char *p;\r
6058 \r
6059   switch (message) {\r
6060   case WM_INITDIALOG:\r
6061     /* Center the dialog */\r
6062     CenterWindow (hDlg, GetDesktopWindow());\r
6063     Translate(hDlg, DLG_Startup);\r
6064     /* Initialize the dialog items */\r
6065     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6066                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6067                   firstChessProgramNames);\r
6068     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6069                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6070                   secondChessProgramNames);\r
6071     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6072     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6073       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6074     if (*appData.icsHelper != NULLCHAR) {\r
6075       char *q = QuoteForFilename(appData.icsHelper);\r
6076       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6077     }\r
6078     if (*appData.icsHost == NULLCHAR) {\r
6079       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6080       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6081     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6082       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6083       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6084     }\r
6085 \r
6086     if (appData.icsActive) {\r
6087       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6088     }\r
6089     else if (appData.noChessProgram) {\r
6090       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6091     }\r
6092     else {\r
6093       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6094     }\r
6095 \r
6096     SetStartupDialogEnables(hDlg);\r
6097     return TRUE;\r
6098 \r
6099   case WM_COMMAND:\r
6100     switch (LOWORD(wParam)) {\r
6101     case IDOK:\r
6102       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6103         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6104         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6105         p = buf;\r
6106         ParseArgs(StringGet, &p);\r
6107         safeStrCpy(buf, "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6108         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6109         p = buf;\r
6110         ParseArgs(StringGet, &p);\r
6111         appData.noChessProgram = FALSE;\r
6112         appData.icsActive = FALSE;\r
6113       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6114         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6115         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6116         p = buf;\r
6117         ParseArgs(StringGet, &p);\r
6118         if (appData.zippyPlay) {\r
6119           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6120           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6121           p = buf;\r
6122           ParseArgs(StringGet, &p);\r
6123         }\r
6124       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6125         appData.noChessProgram = TRUE;\r
6126         appData.icsActive = FALSE;\r
6127       } else {\r
6128         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6129                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6130         return TRUE;\r
6131       }\r
6132       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6133         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6134         p = buf;\r
6135         ParseArgs(StringGet, &p);\r
6136       }\r
6137       EndDialog(hDlg, TRUE);\r
6138       return TRUE;\r
6139 \r
6140     case IDCANCEL:\r
6141       ExitEvent(0);\r
6142       return TRUE;\r
6143 \r
6144     case IDM_HELPCONTENTS:\r
6145       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6146         MessageBox (GetFocus(),\r
6147                     _("Unable to activate help"),\r
6148                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6149       }\r
6150       break;\r
6151 \r
6152     default:\r
6153       SetStartupDialogEnables(hDlg);\r
6154       break;\r
6155     }\r
6156     break;\r
6157   }\r
6158   return FALSE;\r
6159 }\r
6160 \r
6161 /*---------------------------------------------------------------------------*\\r
6162  *\r
6163  * About box dialog functions\r
6164  *\r
6165 \*---------------------------------------------------------------------------*/\r
6166 \r
6167 /* Process messages for "About" dialog box */\r
6168 LRESULT CALLBACK\r
6169 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6170 {\r
6171   switch (message) {\r
6172   case WM_INITDIALOG: /* message: initialize dialog box */\r
6173     /* Center the dialog over the application window */\r
6174     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6175     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6176     Translate(hDlg, ABOUTBOX);\r
6177     JAWS_COPYRIGHT\r
6178     return (TRUE);\r
6179 \r
6180   case WM_COMMAND: /* message: received a command */\r
6181     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6182         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6183       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6184       return (TRUE);\r
6185     }\r
6186     break;\r
6187   }\r
6188   return (FALSE);\r
6189 }\r
6190 \r
6191 /*---------------------------------------------------------------------------*\\r
6192  *\r
6193  * Comment Dialog functions\r
6194  *\r
6195 \*---------------------------------------------------------------------------*/\r
6196 \r
6197 LRESULT CALLBACK\r
6198 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6199 {\r
6200   static HANDLE hwndText = NULL;\r
6201   int len, newSizeX, newSizeY, flags;\r
6202   static int sizeX, sizeY;\r
6203   char *str;\r
6204   RECT rect;\r
6205   MINMAXINFO *mmi;\r
6206 \r
6207   switch (message) {\r
6208   case WM_INITDIALOG: /* message: initialize dialog box */\r
6209     /* Initialize the dialog items */\r
6210     Translate(hDlg, DLG_EditComment);\r
6211     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6212     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6213     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6214     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6215     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6216     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6217     SetWindowText(hDlg, commentTitle);\r
6218     if (editComment) {\r
6219       SetFocus(hwndText);\r
6220     } else {\r
6221       SetFocus(GetDlgItem(hDlg, IDOK));\r
6222     }\r
6223     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6224                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6225                 MAKELPARAM(FALSE, 0));\r
6226     /* Size and position the dialog */\r
6227     if (!commentDialog) {\r
6228       commentDialog = hDlg;\r
6229       flags = SWP_NOZORDER;\r
6230       GetClientRect(hDlg, &rect);\r
6231       sizeX = rect.right;\r
6232       sizeY = rect.bottom;\r
6233       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6234           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6235         WINDOWPLACEMENT wp;\r
6236         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6237         wp.length = sizeof(WINDOWPLACEMENT);\r
6238         wp.flags = 0;\r
6239         wp.showCmd = SW_SHOW;\r
6240         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6241         wp.rcNormalPosition.left = wpComment.x;\r
6242         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6243         wp.rcNormalPosition.top = wpComment.y;\r
6244         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6245         SetWindowPlacement(hDlg, &wp);\r
6246 \r
6247         GetClientRect(hDlg, &rect);\r
6248         newSizeX = rect.right;\r
6249         newSizeY = rect.bottom;\r
6250         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6251                               newSizeX, newSizeY);\r
6252         sizeX = newSizeX;\r
6253         sizeY = newSizeY;\r
6254       }\r
6255     }\r
6256     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6257     return FALSE;\r
6258 \r
6259   case WM_COMMAND: /* message: received a command */\r
6260     switch (LOWORD(wParam)) {\r
6261     case IDOK:\r
6262       if (editComment) {\r
6263         char *p, *q;\r
6264         /* Read changed options from the dialog box */\r
6265         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6266         len = GetWindowTextLength(hwndText);\r
6267         str = (char *) malloc(len + 1);\r
6268         GetWindowText(hwndText, str, len + 1);\r
6269         p = q = str;\r
6270         while (*q) {\r
6271           if (*q == '\r')\r
6272             q++;\r
6273           else\r
6274             *p++ = *q++;\r
6275         }\r
6276         *p = NULLCHAR;\r
6277         ReplaceComment(commentIndex, str);\r
6278         free(str);\r
6279       }\r
6280       CommentPopDown();\r
6281       return TRUE;\r
6282 \r
6283     case IDCANCEL:\r
6284     case OPT_CancelComment:\r
6285       CommentPopDown();\r
6286       return TRUE;\r
6287 \r
6288     case OPT_ClearComment:\r
6289       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6290       break;\r
6291 \r
6292     case OPT_EditComment:\r
6293       EditCommentEvent();\r
6294       return TRUE;\r
6295 \r
6296     default:\r
6297       break;\r
6298     }\r
6299     break;\r
6300 \r
6301   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6302         if( wParam == OPT_CommentText ) {\r
6303             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6304 \r
6305             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6306                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6307                 POINTL pt;\r
6308                 LRESULT index;\r
6309 \r
6310                 pt.x = LOWORD( lpMF->lParam );\r
6311                 pt.y = HIWORD( lpMF->lParam );\r
6312 \r
6313                 if(lpMF->msg == WM_CHAR) {\r
6314                         CHARRANGE sel;\r
6315                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6316                         index = sel.cpMin;\r
6317                 } else\r
6318                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6319 \r
6320                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6321                 len = GetWindowTextLength(hwndText);\r
6322                 str = (char *) malloc(len + 1);\r
6323                 GetWindowText(hwndText, str, len + 1);\r
6324                 ReplaceComment(commentIndex, str);\r
6325                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6326                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6327                 free(str);\r
6328 \r
6329                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6330                 lpMF->msg = WM_USER;\r
6331 \r
6332                 return TRUE;\r
6333             }\r
6334         }\r
6335         break;\r
6336 \r
6337   case WM_SIZE:\r
6338     newSizeX = LOWORD(lParam);\r
6339     newSizeY = HIWORD(lParam);\r
6340     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6341     sizeX = newSizeX;\r
6342     sizeY = newSizeY;\r
6343     break;\r
6344 \r
6345   case WM_GETMINMAXINFO:\r
6346     /* Prevent resizing window too small */\r
6347     mmi = (MINMAXINFO *) lParam;\r
6348     mmi->ptMinTrackSize.x = 100;\r
6349     mmi->ptMinTrackSize.y = 100;\r
6350     break;\r
6351   }\r
6352   return FALSE;\r
6353 }\r
6354 \r
6355 VOID\r
6356 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6357 {\r
6358   FARPROC lpProc;\r
6359   char *p, *q;\r
6360 \r
6361   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6362 \r
6363   if (str == NULL) str = "";\r
6364   p = (char *) malloc(2 * strlen(str) + 2);\r
6365   q = p;\r
6366   while (*str) {\r
6367     if (*str == '\n') *q++ = '\r';\r
6368     *q++ = *str++;\r
6369   }\r
6370   *q = NULLCHAR;\r
6371   if (commentText != NULL) free(commentText);\r
6372 \r
6373   commentIndex = index;\r
6374   commentTitle = title;\r
6375   commentText = p;\r
6376   editComment = edit;\r
6377 \r
6378   if (commentDialog) {\r
6379     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6380     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6381   } else {\r
6382     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6383     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6384                  hwndMain, (DLGPROC)lpProc);\r
6385     FreeProcInstance(lpProc);\r
6386   }\r
6387   commentUp = TRUE;\r
6388 }\r
6389 \r
6390 \r
6391 /*---------------------------------------------------------------------------*\\r
6392  *\r
6393  * Type-in move dialog functions\r
6394  * \r
6395 \*---------------------------------------------------------------------------*/\r
6396 \r
6397 LRESULT CALLBACK\r
6398 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6399 {\r
6400   char move[MSG_SIZ];\r
6401   HWND hInput;\r
6402   ChessMove moveType;\r
6403   int fromX, fromY, toX, toY;\r
6404   char promoChar;\r
6405 \r
6406   switch (message) {\r
6407   case WM_INITDIALOG:\r
6408     move[0] = (char) lParam;\r
6409     move[1] = NULLCHAR;\r
6410     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6411     Translate(hDlg, DLG_TypeInMove);\r
6412     hInput = GetDlgItem(hDlg, OPT_Move);\r
6413     SetWindowText(hInput, move);\r
6414     SetFocus(hInput);\r
6415     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6416     return FALSE;\r
6417 \r
6418   case WM_COMMAND:\r
6419     switch (LOWORD(wParam)) {\r
6420     case IDOK:
6421 \r
6422       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6423       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6424       { int n; Board board;\r
6425         // [HGM] FENedit\r
6426         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
6427                 EditPositionPasteFEN(move);\r
6428                 EndDialog(hDlg, TRUE);\r
6429                 return TRUE;\r
6430         }\r
6431         // [HGM] movenum: allow move number to be typed in any mode\r
6432         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
6433           ToNrEvent(2*n-1);\r
6434           EndDialog(hDlg, TRUE);\r
6435           return TRUE;\r
6436         }\r
6437       }\r
6438       if (gameMode != EditGame && currentMove != forwardMostMove && \r
6439         gameMode != Training) {\r
6440         DisplayMoveError(_("Displayed move is not current"));\r
6441       } else {\r
6442 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
6443         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6444           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
6445         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
6446         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6447           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
6448           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
6449         } else {\r
6450           DisplayMoveError(_("Could not parse move"));\r
6451         }\r
6452       }\r
6453       EndDialog(hDlg, TRUE);\r
6454       return TRUE;\r
6455     case IDCANCEL:\r
6456       EndDialog(hDlg, FALSE);\r
6457       return TRUE;\r
6458     default:\r
6459       break;\r
6460     }\r
6461     break;\r
6462   }\r
6463   return FALSE;\r
6464 }\r
6465 \r
6466 VOID\r
6467 PopUpMoveDialog(char firstchar)\r
6468 {\r
6469     FARPROC lpProc;\r
6470     \r
6471     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
6472         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
6473         gameMode == AnalyzeMode || gameMode == EditGame || \r
6474         gameMode == EditPosition || gameMode == IcsExamining ||\r
6475         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
6476         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
6477                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
6478                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
6479         gameMode == Training) {\r
6480       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6481       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6482         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6483       FreeProcInstance(lpProc);\r
6484     }\r
6485 }\r
6486 \r
6487 /*---------------------------------------------------------------------------*\\r
6488  *\r
6489  * Type-in name dialog functions\r
6490  * \r
6491 \*---------------------------------------------------------------------------*/\r
6492 \r
6493 LRESULT CALLBACK\r
6494 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6495 {\r
6496   char move[MSG_SIZ];\r
6497   HWND hInput;\r
6498 \r
6499   switch (message) {\r
6500   case WM_INITDIALOG:\r
6501     move[0] = (char) lParam;\r
6502     move[1] = NULLCHAR;\r
6503     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6504     Translate(hDlg, DLG_TypeInName);\r
6505     hInput = GetDlgItem(hDlg, OPT_Name);\r
6506     SetWindowText(hInput, move);\r
6507     SetFocus(hInput);\r
6508     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6509     return FALSE;\r
6510 \r
6511   case WM_COMMAND:\r
6512     switch (LOWORD(wParam)) {\r
6513     case IDOK:\r
6514       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6515       appData.userName = strdup(move);\r
6516       SetUserLogo();\r
6517       SetGameInfo();\r
6518       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6519         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6520         DisplayTitle(move);\r
6521       }\r
6522 \r
6523 \r
6524       EndDialog(hDlg, TRUE);\r
6525       return TRUE;\r
6526     case IDCANCEL:\r
6527       EndDialog(hDlg, FALSE);\r
6528       return TRUE;\r
6529     default:\r
6530       break;\r
6531     }\r
6532     break;\r
6533   }\r
6534   return FALSE;\r
6535 }\r
6536 \r
6537 VOID\r
6538 PopUpNameDialog(char firstchar)\r
6539 {\r
6540     FARPROC lpProc;\r
6541     \r
6542       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6543       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6544         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6545       FreeProcInstance(lpProc);\r
6546 }\r
6547 \r
6548 /*---------------------------------------------------------------------------*\\r
6549  *\r
6550  *  Error dialogs\r
6551  * \r
6552 \*---------------------------------------------------------------------------*/\r
6553 \r
6554 /* Nonmodal error box */\r
6555 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6556                              WPARAM wParam, LPARAM lParam);\r
6557 \r
6558 VOID\r
6559 ErrorPopUp(char *title, char *content)\r
6560 {\r
6561   FARPROC lpProc;\r
6562   char *p, *q;\r
6563   BOOLEAN modal = hwndMain == NULL;\r
6564 \r
6565   p = content;\r
6566   q = errorMessage;\r
6567   while (*p) {\r
6568     if (*p == '\n') {\r
6569       if (modal) {\r
6570         *q++ = ' ';\r
6571         p++;\r
6572       } else {\r
6573         *q++ = '\r';\r
6574         *q++ = *p++;\r
6575       }\r
6576     } else {\r
6577       *q++ = *p++;\r
6578     }\r
6579   }\r
6580   *q = NULLCHAR;\r
6581   strncpy(errorTitle, title, sizeof(errorTitle));\r
6582   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6583   \r
6584   if (modal) {\r
6585     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6586   } else {\r
6587     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6588     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6589                  hwndMain, (DLGPROC)lpProc);\r
6590     FreeProcInstance(lpProc);\r
6591   }\r
6592 }\r
6593 \r
6594 VOID\r
6595 ErrorPopDown()\r
6596 {\r
6597   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6598   if (errorDialog == NULL) return;\r
6599   DestroyWindow(errorDialog);\r
6600   errorDialog = NULL;\r
6601   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6602 }\r
6603 \r
6604 LRESULT CALLBACK\r
6605 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6606 {\r
6607   HANDLE hwndText;\r
6608   RECT rChild;\r
6609 \r
6610   switch (message) {\r
6611   case WM_INITDIALOG:\r
6612     GetWindowRect(hDlg, &rChild);\r
6613 \r
6614     /*\r
6615     SetWindowPos(hDlg, NULL, rChild.left,\r
6616       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6617       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6618     */\r
6619 \r
6620     /* \r
6621         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6622         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6623         and it doesn't work when you resize the dialog.\r
6624         For now, just give it a default position.\r
6625     */\r
6626     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6627     Translate(hDlg, DLG_Error);\r
6628 \r
6629     errorDialog = hDlg;\r
6630     SetWindowText(hDlg, errorTitle);\r
6631     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6632     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6633     return FALSE;\r
6634 \r
6635   case WM_COMMAND:\r
6636     switch (LOWORD(wParam)) {\r
6637     case IDOK:\r
6638     case IDCANCEL:\r
6639       if (errorDialog == hDlg) errorDialog = NULL;\r
6640       DestroyWindow(hDlg);\r
6641       return TRUE;\r
6642 \r
6643     default:\r
6644       break;\r
6645     }\r
6646     break;\r
6647   }\r
6648   return FALSE;\r
6649 }\r
6650 \r
6651 #ifdef GOTHIC\r
6652 HWND gothicDialog = NULL;\r
6653 \r
6654 LRESULT CALLBACK\r
6655 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6656 {\r
6657   HANDLE hwndText;\r
6658   RECT rChild;\r
6659   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6660 \r
6661   switch (message) {\r
6662   case WM_INITDIALOG:\r
6663     GetWindowRect(hDlg, &rChild);\r
6664 \r
6665     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6666                                                              SWP_NOZORDER);\r
6667 \r
6668     /* \r
6669         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6670         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6671         and it doesn't work when you resize the dialog.\r
6672         For now, just give it a default position.\r
6673     */\r
6674     gothicDialog = hDlg;\r
6675     SetWindowText(hDlg, errorTitle);\r
6676     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6677     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6678     return FALSE;\r
6679 \r
6680   case WM_COMMAND:\r
6681     switch (LOWORD(wParam)) {\r
6682     case IDOK:\r
6683     case IDCANCEL:\r
6684       if (errorDialog == hDlg) errorDialog = NULL;\r
6685       DestroyWindow(hDlg);\r
6686       return TRUE;\r
6687 \r
6688     default:\r
6689       break;\r
6690     }\r
6691     break;\r
6692   }\r
6693   return FALSE;\r
6694 }\r
6695 \r
6696 VOID\r
6697 GothicPopUp(char *title, VariantClass variant)\r
6698 {\r
6699   FARPROC lpProc;\r
6700   static char *lastTitle;\r
6701 \r
6702   strncpy(errorTitle, title, sizeof(errorTitle));\r
6703   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6704 \r
6705   if(lastTitle != title && gothicDialog != NULL) {\r
6706     DestroyWindow(gothicDialog);\r
6707     gothicDialog = NULL;\r
6708   }\r
6709   if(variant != VariantNormal && gothicDialog == NULL) {\r
6710     title = lastTitle;\r
6711     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6712     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6713                  hwndMain, (DLGPROC)lpProc);\r
6714     FreeProcInstance(lpProc);\r
6715   }\r
6716 }\r
6717 #endif\r
6718 \r
6719 /*---------------------------------------------------------------------------*\\r
6720  *\r
6721  *  Ics Interaction console functions\r
6722  *\r
6723 \*---------------------------------------------------------------------------*/\r
6724 \r
6725 #define HISTORY_SIZE 64\r
6726 static char *history[HISTORY_SIZE];\r
6727 int histIn = 0, histP = 0;\r
6728 \r
6729 VOID\r
6730 SaveInHistory(char *cmd)\r
6731 {\r
6732   if (history[histIn] != NULL) {\r
6733     free(history[histIn]);\r
6734     history[histIn] = NULL;\r
6735   }\r
6736   if (*cmd == NULLCHAR) return;\r
6737   history[histIn] = StrSave(cmd);\r
6738   histIn = (histIn + 1) % HISTORY_SIZE;\r
6739   if (history[histIn] != NULL) {\r
6740     free(history[histIn]);\r
6741     history[histIn] = NULL;\r
6742   }\r
6743   histP = histIn;\r
6744 }\r
6745 \r
6746 char *\r
6747 PrevInHistory(char *cmd)\r
6748 {\r
6749   int newhp;\r
6750   if (histP == histIn) {\r
6751     if (history[histIn] != NULL) free(history[histIn]);\r
6752     history[histIn] = StrSave(cmd);\r
6753   }\r
6754   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6755   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6756   histP = newhp;\r
6757   return history[histP];\r
6758 }\r
6759 \r
6760 char *\r
6761 NextInHistory()\r
6762 {\r
6763   if (histP == histIn) return NULL;\r
6764   histP = (histP + 1) % HISTORY_SIZE;\r
6765   return history[histP];   \r
6766 }\r
6767 \r
6768 HMENU\r
6769 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6770 {\r
6771   HMENU hmenu, h;\r
6772   int i = 0;\r
6773   hmenu = LoadMenu(hInst, "TextMenu");\r
6774   h = GetSubMenu(hmenu, 0);\r
6775   while (e->item) {\r
6776     if (strcmp(e->item, "-") == 0) {\r
6777       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6778     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6779       int flags = MF_STRING, j = 0;\r
6780       if (e->item[0] == '|') {\r
6781         flags |= MF_MENUBARBREAK;\r
6782         j++;\r
6783       }\r
6784       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6785       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6786     }\r
6787     e++;\r
6788     i++;\r
6789   } \r
6790   return hmenu;\r
6791 }\r
6792 \r
6793 WNDPROC consoleTextWindowProc;\r
6794 \r
6795 void\r
6796 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6797 {\r
6798   char buf[MSG_SIZ], name[MSG_SIZ];\r
6799   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6800   CHARRANGE sel;\r
6801 \r
6802   if (!getname) {\r
6803     SetWindowText(hInput, command);\r
6804     if (immediate) {\r
6805       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6806     } else {\r
6807       sel.cpMin = 999999;\r
6808       sel.cpMax = 999999;\r
6809       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6810       SetFocus(hInput);\r
6811     }\r
6812     return;\r
6813   }    \r
6814   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6815   if (sel.cpMin == sel.cpMax) {\r
6816     /* Expand to surrounding word */\r
6817     TEXTRANGE tr;\r
6818     do {\r
6819       tr.chrg.cpMax = sel.cpMin;\r
6820       tr.chrg.cpMin = --sel.cpMin;\r
6821       if (sel.cpMin < 0) break;\r
6822       tr.lpstrText = name;\r
6823       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6824     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6825     sel.cpMin++;\r
6826 \r
6827     do {\r
6828       tr.chrg.cpMin = sel.cpMax;\r
6829       tr.chrg.cpMax = ++sel.cpMax;\r
6830       tr.lpstrText = name;\r
6831       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6832     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6833     sel.cpMax--;\r
6834 \r
6835     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6836       MessageBeep(MB_ICONEXCLAMATION);\r
6837       return;\r
6838     }\r
6839     tr.chrg = sel;\r
6840     tr.lpstrText = name;\r
6841     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6842   } else {\r
6843     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6844       MessageBeep(MB_ICONEXCLAMATION);\r
6845       return;\r
6846     }\r
6847     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6848   }\r
6849   if (immediate) {\r
6850     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6851     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6852     SetWindowText(hInput, buf);\r
6853     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6854   } else {\r
6855     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6856       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6857     SetWindowText(hInput, buf);\r
6858     sel.cpMin = 999999;\r
6859     sel.cpMax = 999999;\r
6860     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6861     SetFocus(hInput);\r
6862   }\r
6863 }\r
6864 \r
6865 LRESULT CALLBACK \r
6866 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6867 {\r
6868   HWND hInput;\r
6869   CHARRANGE sel;\r
6870 \r
6871   switch (message) {\r
6872   case WM_KEYDOWN:\r
6873     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6874     if(wParam=='R') return 0;\r
6875     switch (wParam) {\r
6876     case VK_PRIOR:\r
6877       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6878       return 0;\r
6879     case VK_NEXT:\r
6880       sel.cpMin = 999999;\r
6881       sel.cpMax = 999999;\r
6882       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6883       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6884       return 0;\r
6885     }\r
6886     break;\r
6887   case WM_CHAR:\r
6888    if(wParam != '\022') {\r
6889     if (wParam == '\t') {\r
6890       if (GetKeyState(VK_SHIFT) < 0) {\r
6891         /* shifted */\r
6892         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6893         if (buttonDesc[0].hwnd) {\r
6894           SetFocus(buttonDesc[0].hwnd);\r
6895         } else {\r
6896           SetFocus(hwndMain);\r
6897         }\r
6898       } else {\r
6899         /* unshifted */\r
6900         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6901       }\r
6902     } else {\r
6903       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6904       JAWS_DELETE( SetFocus(hInput); )\r
6905       SendMessage(hInput, message, wParam, lParam);\r
6906     }\r
6907     return 0;\r
6908    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
6909    lParam = -1;\r
6910   case WM_RBUTTONDOWN:\r
6911     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6912       /* Move selection here if it was empty */\r
6913       POINT pt;\r
6914       pt.x = LOWORD(lParam);\r
6915       pt.y = HIWORD(lParam);\r
6916       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6917       if (sel.cpMin == sel.cpMax) {\r
6918         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6919         sel.cpMax = sel.cpMin;\r
6920         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6921       }\r
6922       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6923 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6924       POINT pt;\r
6925       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6926       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6927       if (sel.cpMin == sel.cpMax) {\r
6928         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6929         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6930       }\r
6931       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6932         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6933       }\r
6934       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6935       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6936       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6937       MenuPopup(hwnd, pt, hmenu, -1);\r
6938 }\r
6939     }\r
6940     return 0;\r
6941   case WM_RBUTTONUP:\r
6942     if (GetKeyState(VK_SHIFT) & ~1) {\r
6943       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6944         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6945     }\r
6946     return 0;\r
6947   case WM_PASTE:\r
6948     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6949     SetFocus(hInput);\r
6950     return SendMessage(hInput, message, wParam, lParam);\r
6951   case WM_MBUTTONDOWN:\r
6952     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6953   case WM_COMMAND:\r
6954     switch (LOWORD(wParam)) {\r
6955     case IDM_QuickPaste:\r
6956       {\r
6957         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6958         if (sel.cpMin == sel.cpMax) {\r
6959           MessageBeep(MB_ICONEXCLAMATION);\r
6960           return 0;\r
6961         }\r
6962         SendMessage(hwnd, WM_COPY, 0, 0);\r
6963         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6964         SendMessage(hInput, WM_PASTE, 0, 0);\r
6965         SetFocus(hInput);\r
6966         return 0;\r
6967       }\r
6968     case IDM_Cut:\r
6969       SendMessage(hwnd, WM_CUT, 0, 0);\r
6970       return 0;\r
6971     case IDM_Paste:\r
6972       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6973       return 0;\r
6974     case IDM_Copy:\r
6975       SendMessage(hwnd, WM_COPY, 0, 0);\r
6976       return 0;\r
6977     default:\r
6978       {\r
6979         int i = LOWORD(wParam) - IDM_CommandX;\r
6980         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6981             icsTextMenuEntry[i].command != NULL) {\r
6982           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6983                    icsTextMenuEntry[i].getname,\r
6984                    icsTextMenuEntry[i].immediate);\r
6985           return 0;\r
6986         }\r
6987       }\r
6988       break;\r
6989     }\r
6990     break;\r
6991   }\r
6992   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
6993 }\r
6994 \r
6995 WNDPROC consoleInputWindowProc;\r
6996 \r
6997 LRESULT CALLBACK\r
6998 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6999 {\r
7000   char buf[MSG_SIZ];\r
7001   char *p;\r
7002   static BOOL sendNextChar = FALSE;\r
7003   static BOOL quoteNextChar = FALSE;\r
7004   InputSource *is = consoleInputSource;\r
7005   CHARFORMAT cf;\r
7006   CHARRANGE sel;\r
7007 \r
7008   switch (message) {\r
7009   case WM_CHAR:\r
7010     if (!appData.localLineEditing || sendNextChar) {\r
7011       is->buf[0] = (CHAR) wParam;\r
7012       is->count = 1;\r
7013       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7014       sendNextChar = FALSE;\r
7015       return 0;\r
7016     }\r
7017     if (quoteNextChar) {\r
7018       buf[0] = (char) wParam;\r
7019       buf[1] = NULLCHAR;\r
7020       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7021       quoteNextChar = FALSE;\r
7022       return 0;\r
7023     }\r
7024     switch (wParam) {\r
7025     case '\r':   /* Enter key */\r
7026       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7027       if (consoleEcho) SaveInHistory(is->buf);\r
7028       is->buf[is->count++] = '\n';\r
7029       is->buf[is->count] = NULLCHAR;\r
7030       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7031       if (consoleEcho) {\r
7032         ConsoleOutput(is->buf, is->count, TRUE);\r
7033       } else if (appData.localLineEditing) {\r
7034         ConsoleOutput("\n", 1, TRUE);\r
7035       }\r
7036       /* fall thru */\r
7037     case '\033': /* Escape key */\r
7038       SetWindowText(hwnd, "");\r
7039       cf.cbSize = sizeof(CHARFORMAT);\r
7040       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7041       if (consoleEcho) {\r
7042         cf.crTextColor = textAttribs[ColorNormal].color;\r
7043       } else {\r
7044         cf.crTextColor = COLOR_ECHOOFF;\r
7045       }\r
7046       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7047       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7048       return 0;\r
7049     case '\t':   /* Tab key */\r
7050       if (GetKeyState(VK_SHIFT) < 0) {\r
7051         /* shifted */\r
7052         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7053       } else {\r
7054         /* unshifted */\r
7055         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7056         if (buttonDesc[0].hwnd) {\r
7057           SetFocus(buttonDesc[0].hwnd);\r
7058         } else {\r
7059           SetFocus(hwndMain);\r
7060         }\r
7061       }\r
7062       return 0;\r
7063     case '\023': /* Ctrl+S */\r
7064       sendNextChar = TRUE;\r
7065       return 0;\r
7066     case '\021': /* Ctrl+Q */\r
7067       quoteNextChar = TRUE;\r
7068       return 0;\r
7069     JAWS_REPLAY\r
7070     default:\r
7071       break;\r
7072     }\r
7073     break;\r
7074   case WM_KEYDOWN:\r
7075     switch (wParam) {\r
7076     case VK_UP:\r
7077       GetWindowText(hwnd, buf, MSG_SIZ);\r
7078       p = PrevInHistory(buf);\r
7079       if (p != NULL) {\r
7080         SetWindowText(hwnd, p);\r
7081         sel.cpMin = 999999;\r
7082         sel.cpMax = 999999;\r
7083         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7084         return 0;\r
7085       }\r
7086       break;\r
7087     case VK_DOWN:\r
7088       p = NextInHistory();\r
7089       if (p != NULL) {\r
7090         SetWindowText(hwnd, p);\r
7091         sel.cpMin = 999999;\r
7092         sel.cpMax = 999999;\r
7093         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7094         return 0;\r
7095       }\r
7096       break;\r
7097     case VK_HOME:\r
7098     case VK_END:\r
7099       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7100       /* fall thru */\r
7101     case VK_PRIOR:\r
7102     case VK_NEXT:\r
7103       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7104       return 0;\r
7105     }\r
7106     break;\r
7107   case WM_MBUTTONDOWN:\r
7108     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7109       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7110     break;\r
7111   case WM_RBUTTONUP:\r
7112     if (GetKeyState(VK_SHIFT) & ~1) {\r
7113       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7114         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7115     } else {\r
7116       POINT pt;\r
7117       HMENU hmenu;\r
7118       hmenu = LoadMenu(hInst, "InputMenu");\r
7119       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7120       if (sel.cpMin == sel.cpMax) {\r
7121         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7122         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7123       }\r
7124       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7125         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7126       }\r
7127       pt.x = LOWORD(lParam);\r
7128       pt.y = HIWORD(lParam);\r
7129       MenuPopup(hwnd, pt, hmenu, -1);\r
7130     }\r
7131     return 0;\r
7132   case WM_COMMAND:\r
7133     switch (LOWORD(wParam)) { \r
7134     case IDM_Undo:\r
7135       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7136       return 0;\r
7137     case IDM_SelectAll:\r
7138       sel.cpMin = 0;\r
7139       sel.cpMax = -1; /*999999?*/\r
7140       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7141       return 0;\r
7142     case IDM_Cut:\r
7143       SendMessage(hwnd, WM_CUT, 0, 0);\r
7144       return 0;\r
7145     case IDM_Paste:\r
7146       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7147       return 0;\r
7148     case IDM_Copy:\r
7149       SendMessage(hwnd, WM_COPY, 0, 0);\r
7150       return 0;\r
7151     }\r
7152     break;\r
7153   }\r
7154   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7155 }\r
7156 \r
7157 #define CO_MAX  100000\r
7158 #define CO_TRIM   1000\r
7159 \r
7160 LRESULT CALLBACK\r
7161 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7162 {\r
7163   static SnapData sd;\r
7164   HWND hText, hInput;\r
7165   RECT rect;\r
7166   static int sizeX, sizeY;\r
7167   int newSizeX, newSizeY;\r
7168   MINMAXINFO *mmi;\r
7169   WORD wMask;\r
7170 \r
7171   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7172   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7173 \r
7174   switch (message) {\r
7175   case WM_NOTIFY:\r
7176     if (((NMHDR*)lParam)->code == EN_LINK)\r
7177     {\r
7178       ENLINK *pLink = (ENLINK*)lParam;\r
7179       if (pLink->msg == WM_LBUTTONUP)\r
7180       {\r
7181         TEXTRANGE tr;\r
7182 \r
7183         tr.chrg = pLink->chrg;\r
7184         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7185         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7186         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7187         free(tr.lpstrText);\r
7188       }\r
7189     }\r
7190     break;\r
7191   case WM_INITDIALOG: /* message: initialize dialog box */\r
7192     hwndConsole = hDlg;\r
7193     SetFocus(hInput);\r
7194     consoleTextWindowProc = (WNDPROC)\r
7195       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7196     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7197     consoleInputWindowProc = (WNDPROC)\r
7198       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7199     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7200     Colorize(ColorNormal, TRUE);\r
7201     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7202     ChangedConsoleFont();\r
7203     GetClientRect(hDlg, &rect);\r
7204     sizeX = rect.right;\r
7205     sizeY = rect.bottom;\r
7206     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7207         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7208       WINDOWPLACEMENT wp;\r
7209       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7210       wp.length = sizeof(WINDOWPLACEMENT);\r
7211       wp.flags = 0;\r
7212       wp.showCmd = SW_SHOW;\r
7213       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7214       wp.rcNormalPosition.left = wpConsole.x;\r
7215       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7216       wp.rcNormalPosition.top = wpConsole.y;\r
7217       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7218       SetWindowPlacement(hDlg, &wp);\r
7219     }\r
7220 \r
7221    // [HGM] Chessknight's change 2004-07-13\r
7222    else { /* Determine Defaults */\r
7223        WINDOWPLACEMENT wp;\r
7224        wpConsole.x = wpMain.width + 1;\r
7225        wpConsole.y = wpMain.y;\r
7226        wpConsole.width = screenWidth -  wpMain.width;\r
7227        wpConsole.height = wpMain.height;\r
7228        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7229        wp.length = sizeof(WINDOWPLACEMENT);\r
7230        wp.flags = 0;\r
7231        wp.showCmd = SW_SHOW;\r
7232        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7233        wp.rcNormalPosition.left = wpConsole.x;\r
7234        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7235        wp.rcNormalPosition.top = wpConsole.y;\r
7236        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7237        SetWindowPlacement(hDlg, &wp);\r
7238     }\r
7239 \r
7240    // Allow hText to highlight URLs and send notifications on them\r
7241    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7242    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7243    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7244    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7245 \r
7246     return FALSE;\r
7247 \r
7248   case WM_SETFOCUS:\r
7249     SetFocus(hInput);\r
7250     return 0;\r
7251 \r
7252   case WM_CLOSE:\r
7253     ExitEvent(0);\r
7254     /* not reached */\r
7255     break;\r
7256 \r
7257   case WM_SIZE:\r
7258     if (IsIconic(hDlg)) break;\r
7259     newSizeX = LOWORD(lParam);\r
7260     newSizeY = HIWORD(lParam);\r
7261     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7262       RECT rectText, rectInput;\r
7263       POINT pt;\r
7264       int newTextHeight, newTextWidth;\r
7265       GetWindowRect(hText, &rectText);\r
7266       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7267       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7268       if (newTextHeight < 0) {\r
7269         newSizeY += -newTextHeight;\r
7270         newTextHeight = 0;\r
7271       }\r
7272       SetWindowPos(hText, NULL, 0, 0,\r
7273         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7274       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7275       pt.x = rectInput.left;\r
7276       pt.y = rectInput.top + newSizeY - sizeY;\r
7277       ScreenToClient(hDlg, &pt);\r
7278       SetWindowPos(hInput, NULL, \r
7279         pt.x, pt.y, /* needs client coords */   \r
7280         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7281         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7282     }\r
7283     sizeX = newSizeX;\r
7284     sizeY = newSizeY;\r
7285     break;\r
7286 \r
7287   case WM_GETMINMAXINFO:\r
7288     /* Prevent resizing window too small */\r
7289     mmi = (MINMAXINFO *) lParam;\r
7290     mmi->ptMinTrackSize.x = 100;\r
7291     mmi->ptMinTrackSize.y = 100;\r
7292     break;\r
7293 \r
7294   /* [AS] Snapping */\r
7295   case WM_ENTERSIZEMOVE:\r
7296     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7297 \r
7298   case WM_SIZING:\r
7299     return OnSizing( &sd, hDlg, wParam, lParam );\r
7300 \r
7301   case WM_MOVING:\r
7302     return OnMoving( &sd, hDlg, wParam, lParam );\r
7303 \r
7304   case WM_EXITSIZEMOVE:\r
7305         UpdateICSWidth(hText);\r
7306     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7307   }\r
7308 \r
7309   return DefWindowProc(hDlg, message, wParam, lParam);\r
7310 }\r
7311 \r
7312 \r
7313 VOID\r
7314 ConsoleCreate()\r
7315 {\r
7316   HWND hCons;\r
7317   if (hwndConsole) return;\r
7318   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7319   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7320 }\r
7321 \r
7322 \r
7323 VOID\r
7324 ConsoleOutput(char* data, int length, int forceVisible)\r
7325 {\r
7326   HWND hText;\r
7327   int trim, exlen;\r
7328   char *p, *q;\r
7329   char buf[CO_MAX+1];\r
7330   POINT pEnd;\r
7331   RECT rect;\r
7332   static int delayLF = 0;\r
7333   CHARRANGE savesel, sel;\r
7334 \r
7335   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7336   p = data;\r
7337   q = buf;\r
7338   if (delayLF) {\r
7339     *q++ = '\r';\r
7340     *q++ = '\n';\r
7341     delayLF = 0;\r
7342   }\r
7343   while (length--) {\r
7344     if (*p == '\n') {\r
7345       if (*++p) {\r
7346         *q++ = '\r';\r
7347         *q++ = '\n';\r
7348       } else {\r
7349         delayLF = 1;\r
7350       }\r
7351     } else if (*p == '\007') {\r
7352        MyPlaySound(&sounds[(int)SoundBell]);\r
7353        p++;\r
7354     } else {\r
7355       *q++ = *p++;\r
7356     }\r
7357   }\r
7358   *q = NULLCHAR;\r
7359   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7360   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7361   /* Save current selection */\r
7362   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7363   exlen = GetWindowTextLength(hText);\r
7364   /* Find out whether current end of text is visible */\r
7365   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7366   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7367   /* Trim existing text if it's too long */\r
7368   if (exlen + (q - buf) > CO_MAX) {\r
7369     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7370     sel.cpMin = 0;\r
7371     sel.cpMax = trim;\r
7372     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7373     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7374     exlen -= trim;\r
7375     savesel.cpMin -= trim;\r
7376     savesel.cpMax -= trim;\r
7377     if (exlen < 0) exlen = 0;\r
7378     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7379     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7380   }\r
7381   /* Append the new text */\r
7382   sel.cpMin = exlen;\r
7383   sel.cpMax = exlen;\r
7384   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7385   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7386   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7387   if (forceVisible || exlen == 0 ||\r
7388       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7389        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7390     /* Scroll to make new end of text visible if old end of text\r
7391        was visible or new text is an echo of user typein */\r
7392     sel.cpMin = 9999999;\r
7393     sel.cpMax = 9999999;\r
7394     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7395     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7396     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7397     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7398   }\r
7399   if (savesel.cpMax == exlen || forceVisible) {\r
7400     /* Move insert point to new end of text if it was at the old\r
7401        end of text or if the new text is an echo of user typein */\r
7402     sel.cpMin = 9999999;\r
7403     sel.cpMax = 9999999;\r
7404     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7405   } else {\r
7406     /* Restore previous selection */\r
7407     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7408   }\r
7409   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7410 }\r
7411 \r
7412 /*---------*/\r
7413 \r
7414 \r
7415 void\r
7416 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7417 {\r
7418   char buf[100];\r
7419   char *str;\r
7420   COLORREF oldFg, oldBg;\r
7421   HFONT oldFont;\r
7422   RECT rect;\r
7423 \r
7424   if(copyNumber > 1)
7425     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7426 \r
7427   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7428   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7429   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7430 \r
7431   rect.left = x;\r
7432   rect.right = x + squareSize;\r
7433   rect.top  = y;\r
7434   rect.bottom = y + squareSize;\r
7435   str = buf;\r
7436 \r
7437   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7438                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7439              y, ETO_CLIPPED|ETO_OPAQUE,\r
7440              &rect, str, strlen(str), NULL);\r
7441 \r
7442   (void) SetTextColor(hdc, oldFg);\r
7443   (void) SetBkColor(hdc, oldBg);\r
7444   (void) SelectObject(hdc, oldFont);\r
7445 }\r
7446 \r
7447 void\r
7448 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7449               RECT *rect, char *color, char *flagFell)\r
7450 {\r
7451   char buf[100];\r
7452   char *str;\r
7453   COLORREF oldFg, oldBg;\r
7454   HFONT oldFont;\r
7455 \r
7456   if (appData.clockMode) {\r
7457     if (tinyLayout)\r
7458       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7459     else\r
7460       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7461     str = buf;\r
7462   } else {\r
7463     str = color;\r
7464   }\r
7465 \r
7466   if (highlight) {\r
7467     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7468     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7469   } else {\r
7470     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7471     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7472   }\r
7473   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7474 \r
7475   JAWS_SILENCE\r
7476 \r
7477   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7478              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7479              rect, str, strlen(str), NULL);\r
7480   if(logoHeight > 0 && appData.clockMode) {\r
7481       RECT r;\r
7482       str += strlen(color)+2;\r
7483       r.top = rect->top + logoHeight/2;\r
7484       r.left = rect->left;\r
7485       r.right = rect->right;\r
7486       r.bottom = rect->bottom;\r
7487       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7488                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7489                  &r, str, strlen(str), NULL);\r
7490   }\r
7491   (void) SetTextColor(hdc, oldFg);\r
7492   (void) SetBkColor(hdc, oldBg);\r
7493   (void) SelectObject(hdc, oldFont);\r
7494 }\r
7495 \r
7496 \r
7497 int\r
7498 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7499            OVERLAPPED *ovl)\r
7500 {\r
7501   int ok, err;\r
7502 \r
7503   /* [AS]  */\r
7504   if( count <= 0 ) {\r
7505     if (appData.debugMode) {\r
7506       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7507     }\r
7508 \r
7509     return ERROR_INVALID_USER_BUFFER;\r
7510   }\r
7511 \r
7512   ResetEvent(ovl->hEvent);\r
7513   ovl->Offset = ovl->OffsetHigh = 0;\r
7514   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7515   if (ok) {\r
7516     err = NO_ERROR;\r
7517   } else {\r
7518     err = GetLastError();\r
7519     if (err == ERROR_IO_PENDING) {\r
7520       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7521       if (ok)\r
7522         err = NO_ERROR;\r
7523       else\r
7524         err = GetLastError();\r
7525     }\r
7526   }\r
7527   return err;\r
7528 }\r
7529 \r
7530 int\r
7531 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7532             OVERLAPPED *ovl)\r
7533 {\r
7534   int ok, err;\r
7535 \r
7536   ResetEvent(ovl->hEvent);\r
7537   ovl->Offset = ovl->OffsetHigh = 0;\r
7538   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7539   if (ok) {\r
7540     err = NO_ERROR;\r
7541   } else {\r
7542     err = GetLastError();\r
7543     if (err == ERROR_IO_PENDING) {\r
7544       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7545       if (ok)\r
7546         err = NO_ERROR;\r
7547       else\r
7548         err = GetLastError();\r
7549     }\r
7550   }\r
7551   return err;\r
7552 }\r
7553 \r
7554 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7555 void CheckForInputBufferFull( InputSource * is )\r
7556 {\r
7557     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7558         /* Look for end of line */\r
7559         char * p = is->buf;\r
7560         \r
7561         while( p < is->next && *p != '\n' ) {\r
7562             p++;\r
7563         }\r
7564 \r
7565         if( p >= is->next ) {\r
7566             if (appData.debugMode) {\r
7567                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7568             }\r
7569 \r
7570             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7571             is->count = (DWORD) -1;\r
7572             is->next = is->buf;\r
7573         }\r
7574     }\r
7575 }\r
7576 \r
7577 DWORD\r
7578 InputThread(LPVOID arg)\r
7579 {\r
7580   InputSource *is;\r
7581   OVERLAPPED ovl;\r
7582 \r
7583   is = (InputSource *) arg;\r
7584   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7585   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7586   while (is->hThread != NULL) {\r
7587     is->error = DoReadFile(is->hFile, is->next,\r
7588                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7589                            &is->count, &ovl);\r
7590     if (is->error == NO_ERROR) {\r
7591       is->next += is->count;\r
7592     } else {\r
7593       if (is->error == ERROR_BROKEN_PIPE) {\r
7594         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7595         is->count = 0;\r
7596       } else {\r
7597         is->count = (DWORD) -1;\r
7598         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7599         break; \r
7600       }\r
7601     }\r
7602 \r
7603     CheckForInputBufferFull( is );\r
7604 \r
7605     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7606 \r
7607     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7608 \r
7609     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7610   }\r
7611 \r
7612   CloseHandle(ovl.hEvent);\r
7613   CloseHandle(is->hFile);\r
7614 \r
7615   if (appData.debugMode) {\r
7616     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7617   }\r
7618 \r
7619   return 0;\r
7620 }\r
7621 \r
7622 \r
7623 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7624 DWORD\r
7625 NonOvlInputThread(LPVOID arg)\r
7626 {\r
7627   InputSource *is;\r
7628   char *p, *q;\r
7629   int i;\r
7630   char prev;\r
7631 \r
7632   is = (InputSource *) arg;\r
7633   while (is->hThread != NULL) {\r
7634     is->error = ReadFile(is->hFile, is->next,\r
7635                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7636                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7637     if (is->error == NO_ERROR) {\r
7638       /* Change CRLF to LF */\r
7639       if (is->next > is->buf) {\r
7640         p = is->next - 1;\r
7641         i = is->count + 1;\r
7642       } else {\r
7643         p = is->next;\r
7644         i = is->count;\r
7645       }\r
7646       q = p;\r
7647       prev = NULLCHAR;\r
7648       while (i > 0) {\r
7649         if (prev == '\r' && *p == '\n') {\r
7650           *(q-1) = '\n';\r
7651           is->count--;\r
7652         } else { \r
7653           *q++ = *p;\r
7654         }\r
7655         prev = *p++;\r
7656         i--;\r
7657       }\r
7658       *q = NULLCHAR;\r
7659       is->next = q;\r
7660     } else {\r
7661       if (is->error == ERROR_BROKEN_PIPE) {\r
7662         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7663         is->count = 0; \r
7664       } else {\r
7665         is->count = (DWORD) -1;\r
7666       }\r
7667     }\r
7668 \r
7669     CheckForInputBufferFull( is );\r
7670 \r
7671     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7672 \r
7673     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7674 \r
7675     if (is->count < 0) break;  /* Quit on error */\r
7676   }\r
7677   CloseHandle(is->hFile);\r
7678   return 0;\r
7679 }\r
7680 \r
7681 DWORD\r
7682 SocketInputThread(LPVOID arg)\r
7683 {\r
7684   InputSource *is;\r
7685 \r
7686   is = (InputSource *) arg;\r
7687   while (is->hThread != NULL) {\r
7688     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7689     if ((int)is->count == SOCKET_ERROR) {\r
7690       is->count = (DWORD) -1;\r
7691       is->error = WSAGetLastError();\r
7692     } else {\r
7693       is->error = NO_ERROR;\r
7694       is->next += is->count;\r
7695       if (is->count == 0 && is->second == is) {\r
7696         /* End of file on stderr; quit with no message */\r
7697         break;\r
7698       }\r
7699     }\r
7700     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7701 \r
7702     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7703 \r
7704     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7705   }\r
7706   return 0;\r
7707 }\r
7708 \r
7709 VOID\r
7710 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7711 {\r
7712   InputSource *is;\r
7713 \r
7714   is = (InputSource *) lParam;\r
7715   if (is->lineByLine) {\r
7716     /* Feed in lines one by one */\r
7717     char *p = is->buf;\r
7718     char *q = p;\r
7719     while (q < is->next) {\r
7720       if (*q++ == '\n') {\r
7721         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7722         p = q;\r
7723       }\r
7724     }\r
7725     \r
7726     /* Move any partial line to the start of the buffer */\r
7727     q = is->buf;\r
7728     while (p < is->next) {\r
7729       *q++ = *p++;\r
7730     }\r
7731     is->next = q;\r
7732 \r
7733     if (is->error != NO_ERROR || is->count == 0) {\r
7734       /* Notify backend of the error.  Note: If there was a partial\r
7735          line at the end, it is not flushed through. */\r
7736       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7737     }\r
7738   } else {\r
7739     /* Feed in the whole chunk of input at once */\r
7740     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7741     is->next = is->buf;\r
7742   }\r
7743 }\r
7744 \r
7745 /*---------------------------------------------------------------------------*\\r
7746  *\r
7747  *  Menu enables. Used when setting various modes.\r
7748  *\r
7749 \*---------------------------------------------------------------------------*/\r
7750 \r
7751 typedef struct {\r
7752   int item;\r
7753   int flags;\r
7754 } Enables;\r
7755 \r
7756 VOID\r
7757 GreyRevert(Boolean grey)\r
7758 { // [HGM] vari: for retracting variations in local mode\r
7759   HMENU hmenu = GetMenu(hwndMain);\r
7760   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7761   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7762 }\r
7763 \r
7764 VOID\r
7765 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7766 {\r
7767   while (enab->item > 0) {\r
7768     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7769     enab++;\r
7770   }\r
7771 }\r
7772 \r
7773 Enables gnuEnables[] = {\r
7774   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7775   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7776   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7777   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7778   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7779   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7780   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7781   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7782   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7783   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7784   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7785   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7786   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7787   { -1, -1 }\r
7788 };\r
7789 \r
7790 Enables icsEnables[] = {\r
7791   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7792   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7793   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7794   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7795   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7796   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7797   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7798   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7799   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7800   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7801   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7802   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7803   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7804   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7805   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7806   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7807   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7808   { -1, -1 }\r
7809 };\r
7810 \r
7811 #if ZIPPY\r
7812 Enables zippyEnables[] = {\r
7813   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7814   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7815   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7816   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7817   { -1, -1 }\r
7818 };\r
7819 #endif\r
7820 \r
7821 Enables ncpEnables[] = {\r
7822   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7823   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7824   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7825   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7826   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7827   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7828   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7829   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7830   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7831   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7832   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7833   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7834   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7835   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7836   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7837   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7838   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7839   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7840   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7841   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7842   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7843   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7844   { -1, -1 }\r
7845 };\r
7846 \r
7847 Enables trainingOnEnables[] = {\r
7848   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7849   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7850   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7851   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7852   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7853   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7854   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7855   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7856   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7857   { -1, -1 }\r
7858 };\r
7859 \r
7860 Enables trainingOffEnables[] = {\r
7861   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7862   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7863   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7864   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7865   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7866   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7867   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7868   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7869   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7870   { -1, -1 }\r
7871 };\r
7872 \r
7873 /* These modify either ncpEnables or gnuEnables */\r
7874 Enables cmailEnables[] = {\r
7875   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7876   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7877   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7878   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7879   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7880   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7881   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7882   { -1, -1 }\r
7883 };\r
7884 \r
7885 Enables machineThinkingEnables[] = {\r
7886   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7887   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7888   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7889   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7890   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7891   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7892   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7893   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7894   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7895   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7896   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7897   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7898   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7899   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7900   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7901   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7902   { -1, -1 }\r
7903 };\r
7904 \r
7905 Enables userThinkingEnables[] = {\r
7906   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7907   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7908   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7909   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7910   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7911   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7912   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7913   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7914   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7915   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7916   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7917   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7918   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7919   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7920   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7921   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7922   { -1, -1 }\r
7923 };\r
7924 \r
7925 /*---------------------------------------------------------------------------*\\r
7926  *\r
7927  *  Front-end interface functions exported by XBoard.\r
7928  *  Functions appear in same order as prototypes in frontend.h.\r
7929  * \r
7930 \*---------------------------------------------------------------------------*/\r
7931 VOID\r
7932 ModeHighlight()\r
7933 {\r
7934   static UINT prevChecked = 0;\r
7935   static int prevPausing = 0;\r
7936   UINT nowChecked;\r
7937 \r
7938   if (pausing != prevPausing) {\r
7939     prevPausing = pausing;\r
7940     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7941                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7942     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7943   }\r
7944 \r
7945   switch (gameMode) {\r
7946   case BeginningOfGame:\r
7947     if (appData.icsActive)\r
7948       nowChecked = IDM_IcsClient;\r
7949     else if (appData.noChessProgram)\r
7950       nowChecked = IDM_EditGame;\r
7951     else\r
7952       nowChecked = IDM_MachineBlack;\r
7953     break;\r
7954   case MachinePlaysBlack:\r
7955     nowChecked = IDM_MachineBlack;\r
7956     break;\r
7957   case MachinePlaysWhite:\r
7958     nowChecked = IDM_MachineWhite;\r
7959     break;\r
7960   case TwoMachinesPlay:\r
7961     nowChecked = matchMode ? IDM_Match : IDM_TwoMachines; // [HGM] match\r
7962     break;\r
7963   case AnalyzeMode:\r
7964     nowChecked = IDM_AnalysisMode;\r
7965     break;\r
7966   case AnalyzeFile:\r
7967     nowChecked = IDM_AnalyzeFile;\r
7968     break;\r
7969   case EditGame:\r
7970     nowChecked = IDM_EditGame;\r
7971     break;\r
7972   case PlayFromGameFile:\r
7973     nowChecked = IDM_LoadGame;\r
7974     break;\r
7975   case EditPosition:\r
7976     nowChecked = IDM_EditPosition;\r
7977     break;\r
7978   case Training:\r
7979     nowChecked = IDM_Training;\r
7980     break;\r
7981   case IcsPlayingWhite:\r
7982   case IcsPlayingBlack:\r
7983   case IcsObserving:\r
7984   case IcsIdle:\r
7985     nowChecked = IDM_IcsClient;\r
7986     break;\r
7987   default:\r
7988   case EndOfGame:\r
7989     nowChecked = 0;\r
7990     break;\r
7991   }\r
7992   if (prevChecked != 0)\r
7993     (void) CheckMenuItem(GetMenu(hwndMain),\r
7994                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
7995   if (nowChecked != 0)\r
7996     (void) CheckMenuItem(GetMenu(hwndMain),\r
7997                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
7998 \r
7999   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8000     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8001                           MF_BYCOMMAND|MF_ENABLED);\r
8002   } else {\r
8003     (void) EnableMenuItem(GetMenu(hwndMain), \r
8004                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8005   }\r
8006 \r
8007   prevChecked = nowChecked;\r
8008 \r
8009   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8010   if (appData.icsActive) {\r
8011        if (appData.icsEngineAnalyze) {\r
8012                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8013                        MF_BYCOMMAND|MF_CHECKED);\r
8014        } else {\r
8015                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8016                        MF_BYCOMMAND|MF_UNCHECKED);\r
8017        }\r
8018   }\r
8019   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8020 }\r
8021 \r
8022 VOID\r
8023 SetICSMode()\r
8024 {\r
8025   HMENU hmenu = GetMenu(hwndMain);\r
8026   SetMenuEnables(hmenu, icsEnables);\r
8027   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8028     MF_BYCOMMAND|MF_ENABLED);\r
8029 #if ZIPPY\r
8030   if (appData.zippyPlay) {\r
8031     SetMenuEnables(hmenu, zippyEnables);\r
8032     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8033          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8034           MF_BYCOMMAND|MF_ENABLED);\r
8035   }\r
8036 #endif\r
8037 }\r
8038 \r
8039 VOID\r
8040 SetGNUMode()\r
8041 {\r
8042   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8043 }\r
8044 \r
8045 VOID\r
8046 SetNCPMode()\r
8047 {\r
8048   HMENU hmenu = GetMenu(hwndMain);\r
8049   SetMenuEnables(hmenu, ncpEnables);\r
8050     DrawMenuBar(hwndMain);\r
8051 }\r
8052 \r
8053 VOID\r
8054 SetCmailMode()\r
8055 {\r
8056   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8057 }\r
8058 \r
8059 VOID \r
8060 SetTrainingModeOn()\r
8061 {\r
8062   int i;\r
8063   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8064   for (i = 0; i < N_BUTTONS; i++) {\r
8065     if (buttonDesc[i].hwnd != NULL)\r
8066       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8067   }\r
8068   CommentPopDown();\r
8069 }\r
8070 \r
8071 VOID SetTrainingModeOff()\r
8072 {\r
8073   int i;\r
8074   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8075   for (i = 0; i < N_BUTTONS; i++) {\r
8076     if (buttonDesc[i].hwnd != NULL)\r
8077       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8078   }\r
8079 }\r
8080 \r
8081 \r
8082 VOID\r
8083 SetUserThinkingEnables()\r
8084 {\r
8085   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8086 }\r
8087 \r
8088 VOID\r
8089 SetMachineThinkingEnables()\r
8090 {\r
8091   HMENU hMenu = GetMenu(hwndMain);\r
8092   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8093 \r
8094   SetMenuEnables(hMenu, machineThinkingEnables);\r
8095 \r
8096   if (gameMode == MachinePlaysBlack) {\r
8097     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8098   } else if (gameMode == MachinePlaysWhite) {\r
8099     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8100   } else if (gameMode == TwoMachinesPlay) {\r
8101     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8102   }\r
8103 }\r
8104 \r
8105 \r
8106 VOID\r
8107 DisplayTitle(char *str)\r
8108 {\r
8109   char title[MSG_SIZ], *host;\r
8110   if (str[0] != NULLCHAR) {\r
8111     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8112   } else if (appData.icsActive) {\r
8113     if (appData.icsCommPort[0] != NULLCHAR)\r
8114       host = "ICS";\r
8115     else \r
8116       host = appData.icsHost;\r
8117       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8118   } else if (appData.noChessProgram) {\r
8119     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8120   } else {\r
8121     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8122     strcat(title, ": ");\r
8123     strcat(title, first.tidy);\r
8124   }\r
8125   SetWindowText(hwndMain, title);\r
8126 }\r
8127 \r
8128 \r
8129 VOID\r
8130 DisplayMessage(char *str1, char *str2)\r
8131 {\r
8132   HDC hdc;\r
8133   HFONT oldFont;\r
8134   int remain = MESSAGE_TEXT_MAX - 1;\r
8135   int len;\r
8136 \r
8137   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8138   messageText[0] = NULLCHAR;\r
8139   if (*str1) {\r
8140     len = strlen(str1);\r
8141     if (len > remain) len = remain;\r
8142     strncpy(messageText, str1, len);\r
8143     messageText[len] = NULLCHAR;\r
8144     remain -= len;\r
8145   }\r
8146   if (*str2 && remain >= 2) {\r
8147     if (*str1) {\r
8148       strcat(messageText, "  ");\r
8149       remain -= 2;\r
8150     }\r
8151     len = strlen(str2);\r
8152     if (len > remain) len = remain;\r
8153     strncat(messageText, str2, len);\r
8154   }\r
8155   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8156 \r
8157   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8158 \r
8159   SAYMACHINEMOVE();\r
8160 \r
8161   hdc = GetDC(hwndMain);\r
8162   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8163   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8164              &messageRect, messageText, strlen(messageText), NULL);\r
8165   (void) SelectObject(hdc, oldFont);\r
8166   (void) ReleaseDC(hwndMain, hdc);\r
8167 }\r
8168 \r
8169 VOID\r
8170 DisplayError(char *str, int error)\r
8171 {\r
8172   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8173   int len;\r
8174 \r
8175   if (error == 0) {\r
8176     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8177   } else {\r
8178     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8179                         NULL, error, LANG_NEUTRAL,\r
8180                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8181     if (len > 0) {\r
8182       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8183     } else {\r
8184       ErrorMap *em = errmap;\r
8185       while (em->err != 0 && em->err != error) em++;\r
8186       if (em->err != 0) {\r
8187         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8188       } else {\r
8189         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8190       }\r
8191     }\r
8192   }\r
8193   \r
8194   ErrorPopUp(_("Error"), buf);\r
8195 }\r
8196 \r
8197 \r
8198 VOID\r
8199 DisplayMoveError(char *str)\r
8200 {\r
8201   fromX = fromY = -1;\r
8202   ClearHighlights();\r
8203   DrawPosition(FALSE, NULL);\r
8204   if (appData.popupMoveErrors) {\r
8205     ErrorPopUp(_("Error"), str);\r
8206   } else {\r
8207     DisplayMessage(str, "");\r
8208     moveErrorMessageUp = TRUE;\r
8209   }\r
8210 }\r
8211 \r
8212 VOID\r
8213 DisplayFatalError(char *str, int error, int exitStatus)\r
8214 {\r
8215   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8216   int len;\r
8217   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8218 \r
8219   if (error != 0) {\r
8220     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8221                         NULL, error, LANG_NEUTRAL,\r
8222                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8223     if (len > 0) {\r
8224       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8225     } else {\r
8226       ErrorMap *em = errmap;\r
8227       while (em->err != 0 && em->err != error) em++;\r
8228       if (em->err != 0) {\r
8229         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8230       } else {\r
8231         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8232       }\r
8233     }\r
8234     str = buf;\r
8235   }\r
8236   if (appData.debugMode) {\r
8237     fprintf(debugFP, "%s: %s\n", label, str);\r
8238   }\r
8239   if (appData.popupExitMessage) {\r
8240     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8241                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8242   }\r
8243   ExitEvent(exitStatus);\r
8244 }\r
8245 \r
8246 \r
8247 VOID\r
8248 DisplayInformation(char *str)\r
8249 {\r
8250   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8251 }\r
8252 \r
8253 \r
8254 VOID\r
8255 DisplayNote(char *str)\r
8256 {\r
8257   ErrorPopUp(_("Note"), str);\r
8258 }\r
8259 \r
8260 \r
8261 typedef struct {\r
8262   char *title, *question, *replyPrefix;\r
8263   ProcRef pr;\r
8264 } QuestionParams;\r
8265 \r
8266 LRESULT CALLBACK\r
8267 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8268 {\r
8269   static QuestionParams *qp;\r
8270   char reply[MSG_SIZ];\r
8271   int len, err;\r
8272 \r
8273   switch (message) {\r
8274   case WM_INITDIALOG:\r
8275     qp = (QuestionParams *) lParam;\r
8276     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8277     Translate(hDlg, DLG_Question);\r
8278     SetWindowText(hDlg, qp->title);\r
8279     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8280     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8281     return FALSE;\r
8282 \r
8283   case WM_COMMAND:\r
8284     switch (LOWORD(wParam)) {\r
8285     case IDOK:\r
8286       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8287       if (*reply) strcat(reply, " ");\r
8288       len = strlen(reply);\r
8289       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8290       strcat(reply, "\n");\r
8291       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8292       EndDialog(hDlg, TRUE);\r
8293       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8294       return TRUE;\r
8295     case IDCANCEL:\r
8296       EndDialog(hDlg, FALSE);\r
8297       return TRUE;\r
8298     default:\r
8299       break;\r
8300     }\r
8301     break;\r
8302   }\r
8303   return FALSE;\r
8304 }\r
8305 \r
8306 VOID\r
8307 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8308 {\r
8309     QuestionParams qp;\r
8310     FARPROC lpProc;\r
8311     \r
8312     qp.title = title;\r
8313     qp.question = question;\r
8314     qp.replyPrefix = replyPrefix;\r
8315     qp.pr = pr;\r
8316     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8317     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8318       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8319     FreeProcInstance(lpProc);\r
8320 }\r
8321 \r
8322 /* [AS] Pick FRC position */\r
8323 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8324 {\r
8325     static int * lpIndexFRC;\r
8326     BOOL index_is_ok;\r
8327     char buf[16];\r
8328 \r
8329     switch( message )\r
8330     {\r
8331     case WM_INITDIALOG:\r
8332         lpIndexFRC = (int *) lParam;\r
8333 \r
8334         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8335         Translate(hDlg, DLG_NewGameFRC);\r
8336 \r
8337         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8338         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8339         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8340         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8341 \r
8342         break;\r
8343 \r
8344     case WM_COMMAND:\r
8345         switch( LOWORD(wParam) ) {\r
8346         case IDOK:\r
8347             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8348             EndDialog( hDlg, 0 );\r
8349             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8350             return TRUE;\r
8351         case IDCANCEL:\r
8352             EndDialog( hDlg, 1 );   \r
8353             return TRUE;\r
8354         case IDC_NFG_Edit:\r
8355             if( HIWORD(wParam) == EN_CHANGE ) {\r
8356                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8357 \r
8358                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8359             }\r
8360             return TRUE;\r
8361         case IDC_NFG_Random:\r
8362           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8363             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8364             return TRUE;\r
8365         }\r
8366 \r
8367         break;\r
8368     }\r
8369 \r
8370     return FALSE;\r
8371 }\r
8372 \r
8373 int NewGameFRC()\r
8374 {\r
8375     int result;\r
8376     int index = appData.defaultFrcPosition;\r
8377     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8378 \r
8379     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8380 \r
8381     if( result == 0 ) {\r
8382         appData.defaultFrcPosition = index;\r
8383     }\r
8384 \r
8385     return result;\r
8386 }\r
8387 \r
8388 /* [AS] Game list options. Refactored by HGM */\r
8389 \r
8390 HWND gameListOptionsDialog;\r
8391 \r
8392 // low-level front-end: clear text edit / list widget\r
8393 void\r
8394 GLT_ClearList()\r
8395 {\r
8396     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8397 }\r
8398 \r
8399 // low-level front-end: clear text edit / list widget\r
8400 void\r
8401 GLT_DeSelectList()\r
8402 {\r
8403     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8404 }\r
8405 \r
8406 // low-level front-end: append line to text edit / list widget\r
8407 void\r
8408 GLT_AddToList( char *name )\r
8409 {\r
8410     if( name != 0 ) {\r
8411             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8412     }\r
8413 }\r
8414 \r
8415 // low-level front-end: get line from text edit / list widget\r
8416 Boolean\r
8417 GLT_GetFromList( int index, char *name )\r
8418 {\r
8419     if( name != 0 ) {\r
8420             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8421                 return TRUE;\r
8422     }\r
8423     return FALSE;\r
8424 }\r
8425 \r
8426 void GLT_MoveSelection( HWND hDlg, int delta )\r
8427 {\r
8428     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8429     int idx2 = idx1 + delta;\r
8430     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8431 \r
8432     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8433         char buf[128];\r
8434 \r
8435         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8436         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8437         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8438         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8439     }\r
8440 }\r
8441 \r
8442 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8443 {\r
8444     switch( message )\r
8445     {\r
8446     case WM_INITDIALOG:\r
8447         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8448         \r
8449         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8450         Translate(hDlg, DLG_GameListOptions);\r
8451 \r
8452         /* Initialize list */\r
8453         GLT_TagsToList( lpUserGLT );\r
8454 \r
8455         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8456 \r
8457         break;\r
8458 \r
8459     case WM_COMMAND:\r
8460         switch( LOWORD(wParam) ) {\r
8461         case IDOK:\r
8462             GLT_ParseList();\r
8463             EndDialog( hDlg, 0 );\r
8464             return TRUE;\r
8465         case IDCANCEL:\r
8466             EndDialog( hDlg, 1 );\r
8467             return TRUE;\r
8468 \r
8469         case IDC_GLT_Default:\r
8470             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8471             return TRUE;\r
8472 \r
8473         case IDC_GLT_Restore:\r
8474             GLT_TagsToList( appData.gameListTags );\r
8475             return TRUE;\r
8476 \r
8477         case IDC_GLT_Up:\r
8478             GLT_MoveSelection( hDlg, -1 );\r
8479             return TRUE;\r
8480 \r
8481         case IDC_GLT_Down:\r
8482             GLT_MoveSelection( hDlg, +1 );\r
8483             return TRUE;\r
8484         }\r
8485 \r
8486         break;\r
8487     }\r
8488 \r
8489     return FALSE;\r
8490 }\r
8491 \r
8492 int GameListOptions()\r
8493 {\r
8494     int result;\r
8495     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8496 \r
8497       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8498 \r
8499     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8500 \r
8501     if( result == 0 ) {\r
8502         /* [AS] Memory leak here! */\r
8503         appData.gameListTags = strdup( lpUserGLT ); \r
8504     }\r
8505 \r
8506     return result;\r
8507 }\r
8508 \r
8509 VOID\r
8510 DisplayIcsInteractionTitle(char *str)\r
8511 {\r
8512   char consoleTitle[MSG_SIZ];\r
8513 \r
8514     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8515   SetWindowText(hwndConsole, consoleTitle);\r
8516 }\r
8517 \r
8518 void\r
8519 DrawPosition(int fullRedraw, Board board)\r
8520 {\r
8521   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8522 }\r
8523 \r
8524 void NotifyFrontendLogin()\r
8525 {\r
8526         if (hwndConsole)\r
8527                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8528 }\r
8529 \r
8530 VOID\r
8531 ResetFrontEnd()\r
8532 {\r
8533   fromX = fromY = -1;\r
8534   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8535     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8536     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8537     dragInfo.lastpos = dragInfo.pos;\r
8538     dragInfo.start.x = dragInfo.start.y = -1;\r
8539     dragInfo.from = dragInfo.start;\r
8540     ReleaseCapture();\r
8541     DrawPosition(TRUE, NULL);\r
8542   }\r
8543   TagsPopDown();\r
8544 }\r
8545 \r
8546 \r
8547 VOID\r
8548 CommentPopUp(char *title, char *str)\r
8549 {\r
8550   HWND hwnd = GetActiveWindow();\r
8551   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8552   SAY(str);\r
8553   SetActiveWindow(hwnd);\r
8554 }\r
8555 \r
8556 VOID\r
8557 CommentPopDown(void)\r
8558 {\r
8559   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8560   if (commentDialog) {\r
8561     ShowWindow(commentDialog, SW_HIDE);\r
8562   }\r
8563   commentUp = FALSE;\r
8564 }\r
8565 \r
8566 VOID\r
8567 EditCommentPopUp(int index, char *title, char *str)\r
8568 {\r
8569   EitherCommentPopUp(index, title, str, TRUE);\r
8570 }\r
8571 \r
8572 \r
8573 VOID\r
8574 RingBell()\r
8575 {\r
8576   MyPlaySound(&sounds[(int)SoundMove]);\r
8577 }\r
8578 \r
8579 VOID PlayIcsWinSound()\r
8580 {\r
8581   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8582 }\r
8583 \r
8584 VOID PlayIcsLossSound()\r
8585 {\r
8586   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8587 }\r
8588 \r
8589 VOID PlayIcsDrawSound()\r
8590 {\r
8591   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8592 }\r
8593 \r
8594 VOID PlayIcsUnfinishedSound()\r
8595 {\r
8596   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8597 }\r
8598 \r
8599 VOID\r
8600 PlayAlarmSound()\r
8601 {\r
8602   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8603 }\r
8604 \r
8605 \r
8606 VOID\r
8607 EchoOn()\r
8608 {\r
8609   HWND hInput;\r
8610   consoleEcho = TRUE;\r
8611   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8612   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8613   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8614 }\r
8615 \r
8616 \r
8617 VOID\r
8618 EchoOff()\r
8619 {\r
8620   CHARFORMAT cf;\r
8621   HWND hInput;\r
8622   consoleEcho = FALSE;\r
8623   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8624   /* This works OK: set text and background both to the same color */\r
8625   cf = consoleCF;\r
8626   cf.crTextColor = COLOR_ECHOOFF;\r
8627   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8628   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8629 }\r
8630 \r
8631 /* No Raw()...? */\r
8632 \r
8633 void Colorize(ColorClass cc, int continuation)\r
8634 {\r
8635   currentColorClass = cc;\r
8636   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8637   consoleCF.crTextColor = textAttribs[cc].color;\r
8638   consoleCF.dwEffects = textAttribs[cc].effects;\r
8639   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8640 }\r
8641 \r
8642 char *\r
8643 UserName()\r
8644 {\r
8645   static char buf[MSG_SIZ];\r
8646   DWORD bufsiz = MSG_SIZ;\r
8647 \r
8648   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8649         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8650   }\r
8651   if (!GetUserName(buf, &bufsiz)) {\r
8652     /*DisplayError("Error getting user name", GetLastError());*/\r
8653     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8654   }\r
8655   return buf;\r
8656 }\r
8657 \r
8658 char *\r
8659 HostName()\r
8660 {\r
8661   static char buf[MSG_SIZ];\r
8662   DWORD bufsiz = MSG_SIZ;\r
8663 \r
8664   if (!GetComputerName(buf, &bufsiz)) {\r
8665     /*DisplayError("Error getting host name", GetLastError());*/\r
8666     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8667   }\r
8668   return buf;\r
8669 }\r
8670 \r
8671 \r
8672 int\r
8673 ClockTimerRunning()\r
8674 {\r
8675   return clockTimerEvent != 0;\r
8676 }\r
8677 \r
8678 int\r
8679 StopClockTimer()\r
8680 {\r
8681   if (clockTimerEvent == 0) return FALSE;\r
8682   KillTimer(hwndMain, clockTimerEvent);\r
8683   clockTimerEvent = 0;\r
8684   return TRUE;\r
8685 }\r
8686 \r
8687 void\r
8688 StartClockTimer(long millisec)\r
8689 {\r
8690   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8691                              (UINT) millisec, NULL);\r
8692 }\r
8693 \r
8694 void\r
8695 DisplayWhiteClock(long timeRemaining, int highlight)\r
8696 {\r
8697   HDC hdc;\r
8698   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8699 \r
8700   if(appData.noGUI) return;\r
8701   hdc = GetDC(hwndMain);\r
8702   if (!IsIconic(hwndMain)) {\r
8703     DisplayAClock(hdc, timeRemaining, highlight, \r
8704                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8705   }\r
8706   if (highlight && iconCurrent == iconBlack) {\r
8707     iconCurrent = iconWhite;\r
8708     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8709     if (IsIconic(hwndMain)) {\r
8710       DrawIcon(hdc, 2, 2, iconCurrent);\r
8711     }\r
8712   }\r
8713   (void) ReleaseDC(hwndMain, hdc);\r
8714   if (hwndConsole)\r
8715     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8716 }\r
8717 \r
8718 void\r
8719 DisplayBlackClock(long timeRemaining, int highlight)\r
8720 {\r
8721   HDC hdc;\r
8722   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8723 \r
8724   if(appData.noGUI) return;\r
8725   hdc = GetDC(hwndMain);\r
8726   if (!IsIconic(hwndMain)) {\r
8727     DisplayAClock(hdc, timeRemaining, highlight, \r
8728                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8729   }\r
8730   if (highlight && iconCurrent == iconWhite) {\r
8731     iconCurrent = iconBlack;\r
8732     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8733     if (IsIconic(hwndMain)) {\r
8734       DrawIcon(hdc, 2, 2, iconCurrent);\r
8735     }\r
8736   }\r
8737   (void) ReleaseDC(hwndMain, hdc);\r
8738   if (hwndConsole)\r
8739     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8740 }\r
8741 \r
8742 \r
8743 int\r
8744 LoadGameTimerRunning()\r
8745 {\r
8746   return loadGameTimerEvent != 0;\r
8747 }\r
8748 \r
8749 int\r
8750 StopLoadGameTimer()\r
8751 {\r
8752   if (loadGameTimerEvent == 0) return FALSE;\r
8753   KillTimer(hwndMain, loadGameTimerEvent);\r
8754   loadGameTimerEvent = 0;\r
8755   return TRUE;\r
8756 }\r
8757 \r
8758 void\r
8759 StartLoadGameTimer(long millisec)\r
8760 {\r
8761   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8762                                 (UINT) millisec, NULL);\r
8763 }\r
8764 \r
8765 void\r
8766 AutoSaveGame()\r
8767 {\r
8768   char *defName;\r
8769   FILE *f;\r
8770   char fileTitle[MSG_SIZ];\r
8771 \r
8772   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8773   f = OpenFileDialog(hwndMain, "a", defName,\r
8774                      appData.oldSaveStyle ? "gam" : "pgn",\r
8775                      GAME_FILT, \r
8776                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8777   if (f != NULL) {\r
8778     SaveGame(f, 0, "");\r
8779     fclose(f);\r
8780   }\r
8781 }\r
8782 \r
8783 \r
8784 void\r
8785 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8786 {\r
8787   if (delayedTimerEvent != 0) {\r
8788     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8789       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8790     }\r
8791     KillTimer(hwndMain, delayedTimerEvent);\r
8792     delayedTimerEvent = 0;\r
8793     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8794     delayedTimerCallback();\r
8795   }\r
8796   delayedTimerCallback = cb;\r
8797   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8798                                 (UINT) millisec, NULL);\r
8799 }\r
8800 \r
8801 DelayedEventCallback\r
8802 GetDelayedEvent()\r
8803 {\r
8804   if (delayedTimerEvent) {\r
8805     return delayedTimerCallback;\r
8806   } else {\r
8807     return NULL;\r
8808   }\r
8809 }\r
8810 \r
8811 void\r
8812 CancelDelayedEvent()\r
8813 {\r
8814   if (delayedTimerEvent) {\r
8815     KillTimer(hwndMain, delayedTimerEvent);\r
8816     delayedTimerEvent = 0;\r
8817   }\r
8818 }\r
8819 \r
8820 DWORD GetWin32Priority(int nice)\r
8821 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8822 /*\r
8823 REALTIME_PRIORITY_CLASS     0x00000100\r
8824 HIGH_PRIORITY_CLASS         0x00000080\r
8825 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8826 NORMAL_PRIORITY_CLASS       0x00000020\r
8827 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8828 IDLE_PRIORITY_CLASS         0x00000040\r
8829 */\r
8830         if (nice < -15) return 0x00000080;\r
8831         if (nice < 0)   return 0x00008000;\r
8832         if (nice == 0)  return 0x00000020;\r
8833         if (nice < 15)  return 0x00004000;\r
8834         return 0x00000040;\r
8835 }\r
8836 \r
8837 /* Start a child process running the given program.\r
8838    The process's standard output can be read from "from", and its\r
8839    standard input can be written to "to".\r
8840    Exit with fatal error if anything goes wrong.\r
8841    Returns an opaque pointer that can be used to destroy the process\r
8842    later.\r
8843 */\r
8844 int\r
8845 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8846 {\r
8847 #define BUFSIZE 4096\r
8848 \r
8849   HANDLE hChildStdinRd, hChildStdinWr,\r
8850     hChildStdoutRd, hChildStdoutWr;\r
8851   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8852   SECURITY_ATTRIBUTES saAttr;\r
8853   BOOL fSuccess;\r
8854   PROCESS_INFORMATION piProcInfo;\r
8855   STARTUPINFO siStartInfo;\r
8856   ChildProc *cp;\r
8857   char buf[MSG_SIZ];\r
8858   DWORD err;\r
8859 \r
8860   if (appData.debugMode) {\r
8861     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8862   }\r
8863 \r
8864   *pr = NoProc;\r
8865 \r
8866   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8867   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8868   saAttr.bInheritHandle = TRUE;\r
8869   saAttr.lpSecurityDescriptor = NULL;\r
8870 \r
8871   /*\r
8872    * The steps for redirecting child's STDOUT:\r
8873    *     1. Create anonymous pipe to be STDOUT for child.\r
8874    *     2. Create a noninheritable duplicate of read handle,\r
8875    *         and close the inheritable read handle.\r
8876    */\r
8877 \r
8878   /* Create a pipe for the child's STDOUT. */\r
8879   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8880     return GetLastError();\r
8881   }\r
8882 \r
8883   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8884   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8885                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8886                              FALSE,     /* not inherited */\r
8887                              DUPLICATE_SAME_ACCESS);\r
8888   if (! fSuccess) {\r
8889     return GetLastError();\r
8890   }\r
8891   CloseHandle(hChildStdoutRd);\r
8892 \r
8893   /*\r
8894    * The steps for redirecting child's STDIN:\r
8895    *     1. Create anonymous pipe to be STDIN for child.\r
8896    *     2. Create a noninheritable duplicate of write handle,\r
8897    *         and close the inheritable write handle.\r
8898    */\r
8899 \r
8900   /* Create a pipe for the child's STDIN. */\r
8901   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8902     return GetLastError();\r
8903   }\r
8904 \r
8905   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8906   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8907                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8908                              FALSE,     /* not inherited */\r
8909                              DUPLICATE_SAME_ACCESS);\r
8910   if (! fSuccess) {\r
8911     return GetLastError();\r
8912   }\r
8913   CloseHandle(hChildStdinWr);\r
8914 \r
8915   /* Arrange to (1) look in dir for the child .exe file, and\r
8916    * (2) have dir be the child's working directory.  Interpret\r
8917    * dir relative to the directory WinBoard loaded from. */\r
8918   GetCurrentDirectory(MSG_SIZ, buf);\r
8919   SetCurrentDirectory(installDir);\r
8920   SetCurrentDirectory(dir);\r
8921 \r
8922   /* Now create the child process. */\r
8923 \r
8924   siStartInfo.cb = sizeof(STARTUPINFO);\r
8925   siStartInfo.lpReserved = NULL;\r
8926   siStartInfo.lpDesktop = NULL;\r
8927   siStartInfo.lpTitle = NULL;\r
8928   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8929   siStartInfo.cbReserved2 = 0;\r
8930   siStartInfo.lpReserved2 = NULL;\r
8931   siStartInfo.hStdInput = hChildStdinRd;\r
8932   siStartInfo.hStdOutput = hChildStdoutWr;\r
8933   siStartInfo.hStdError = hChildStdoutWr;\r
8934 \r
8935   fSuccess = CreateProcess(NULL,\r
8936                            cmdLine,        /* command line */\r
8937                            NULL,           /* process security attributes */\r
8938                            NULL,           /* primary thread security attrs */\r
8939                            TRUE,           /* handles are inherited */\r
8940                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8941                            NULL,           /* use parent's environment */\r
8942                            NULL,\r
8943                            &siStartInfo, /* STARTUPINFO pointer */\r
8944                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
8945 \r
8946   err = GetLastError();\r
8947   SetCurrentDirectory(buf); /* return to prev directory */\r
8948   if (! fSuccess) {\r
8949     return err;\r
8950   }\r
8951 \r
8952   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
8953     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
8954     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
8955   }\r
8956 \r
8957   /* Close the handles we don't need in the parent */\r
8958   CloseHandle(piProcInfo.hThread);\r
8959   CloseHandle(hChildStdinRd);\r
8960   CloseHandle(hChildStdoutWr);\r
8961 \r
8962   /* Prepare return value */\r
8963   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8964   cp->kind = CPReal;\r
8965   cp->hProcess = piProcInfo.hProcess;\r
8966   cp->pid = piProcInfo.dwProcessId;\r
8967   cp->hFrom = hChildStdoutRdDup;\r
8968   cp->hTo = hChildStdinWrDup;\r
8969 \r
8970   *pr = (void *) cp;\r
8971 \r
8972   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
8973      2000 where engines sometimes don't see the initial command(s)\r
8974      from WinBoard and hang.  I don't understand how that can happen,\r
8975      but the Sleep is harmless, so I've put it in.  Others have also\r
8976      reported what may be the same problem, so hopefully this will fix\r
8977      it for them too.  */\r
8978   Sleep(500);\r
8979 \r
8980   return NO_ERROR;\r
8981 }\r
8982 \r
8983 \r
8984 void\r
8985 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
8986 {\r
8987   ChildProc *cp; int result;\r
8988 \r
8989   cp = (ChildProc *) pr;\r
8990   if (cp == NULL) return;\r
8991 \r
8992   switch (cp->kind) {\r
8993   case CPReal:\r
8994     /* TerminateProcess is considered harmful, so... */\r
8995     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
8996     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
8997     /* The following doesn't work because the chess program\r
8998        doesn't "have the same console" as WinBoard.  Maybe\r
8999        we could arrange for this even though neither WinBoard\r
9000        nor the chess program uses a console for stdio? */\r
9001     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9002 \r
9003     /* [AS] Special termination modes for misbehaving programs... */\r
9004     if( signal == 9 ) { \r
9005         result = TerminateProcess( cp->hProcess, 0 );\r
9006 \r
9007         if ( appData.debugMode) {\r
9008             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9009         }\r
9010     }\r
9011     else if( signal == 10 ) {\r
9012         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9013 \r
9014         if( dw != WAIT_OBJECT_0 ) {\r
9015             result = TerminateProcess( cp->hProcess, 0 );\r
9016 \r
9017             if ( appData.debugMode) {\r
9018                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9019             }\r
9020 \r
9021         }\r
9022     }\r
9023 \r
9024     CloseHandle(cp->hProcess);\r
9025     break;\r
9026 \r
9027   case CPComm:\r
9028     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9029     break;\r
9030 \r
9031   case CPSock:\r
9032     closesocket(cp->sock);\r
9033     WSACleanup();\r
9034     break;\r
9035 \r
9036   case CPRcmd:\r
9037     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9038     closesocket(cp->sock);\r
9039     closesocket(cp->sock2);\r
9040     WSACleanup();\r
9041     break;\r
9042   }\r
9043   free(cp);\r
9044 }\r
9045 \r
9046 void\r
9047 InterruptChildProcess(ProcRef pr)\r
9048 {\r
9049   ChildProc *cp;\r
9050 \r
9051   cp = (ChildProc *) pr;\r
9052   if (cp == NULL) return;\r
9053   switch (cp->kind) {\r
9054   case CPReal:\r
9055     /* The following doesn't work because the chess program\r
9056        doesn't "have the same console" as WinBoard.  Maybe\r
9057        we could arrange for this even though neither WinBoard\r
9058        nor the chess program uses a console for stdio */\r
9059     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9060     break;\r
9061 \r
9062   case CPComm:\r
9063   case CPSock:\r
9064     /* Can't interrupt */\r
9065     break;\r
9066 \r
9067   case CPRcmd:\r
9068     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9069     break;\r
9070   }\r
9071 }\r
9072 \r
9073 \r
9074 int\r
9075 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9076 {\r
9077   char cmdLine[MSG_SIZ];\r
9078 \r
9079   if (port[0] == NULLCHAR) {\r
9080     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9081   } else {\r
9082     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9083   }\r
9084   return StartChildProcess(cmdLine, "", pr);\r
9085 }\r
9086 \r
9087 \r
9088 /* Code to open TCP sockets */\r
9089 \r
9090 int\r
9091 OpenTCP(char *host, char *port, ProcRef *pr)\r
9092 {\r
9093   ChildProc *cp;\r
9094   int err;\r
9095   SOCKET s;\r
9096   struct sockaddr_in sa, mysa;\r
9097   struct hostent FAR *hp;\r
9098   unsigned short uport;\r
9099   WORD wVersionRequested;\r
9100   WSADATA wsaData;\r
9101 \r
9102   /* Initialize socket DLL */\r
9103   wVersionRequested = MAKEWORD(1, 1);\r
9104   err = WSAStartup(wVersionRequested, &wsaData);\r
9105   if (err != 0) return err;\r
9106 \r
9107   /* Make socket */\r
9108   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9109     err = WSAGetLastError();\r
9110     WSACleanup();\r
9111     return err;\r
9112   }\r
9113 \r
9114   /* Bind local address using (mostly) don't-care values.\r
9115    */\r
9116   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9117   mysa.sin_family = AF_INET;\r
9118   mysa.sin_addr.s_addr = INADDR_ANY;\r
9119   uport = (unsigned short) 0;\r
9120   mysa.sin_port = htons(uport);\r
9121   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9122       == SOCKET_ERROR) {\r
9123     err = WSAGetLastError();\r
9124     WSACleanup();\r
9125     return err;\r
9126   }\r
9127 \r
9128   /* Resolve remote host name */\r
9129   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9130   if (!(hp = gethostbyname(host))) {\r
9131     unsigned int b0, b1, b2, b3;\r
9132 \r
9133     err = WSAGetLastError();\r
9134 \r
9135     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9136       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9137       hp->h_addrtype = AF_INET;\r
9138       hp->h_length = 4;\r
9139       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9140       hp->h_addr_list[0] = (char *) malloc(4);\r
9141       hp->h_addr_list[0][0] = (char) b0;\r
9142       hp->h_addr_list[0][1] = (char) b1;\r
9143       hp->h_addr_list[0][2] = (char) b2;\r
9144       hp->h_addr_list[0][3] = (char) b3;\r
9145     } else {\r
9146       WSACleanup();\r
9147       return err;\r
9148     }\r
9149   }\r
9150   sa.sin_family = hp->h_addrtype;\r
9151   uport = (unsigned short) atoi(port);\r
9152   sa.sin_port = htons(uport);\r
9153   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9154 \r
9155   /* Make connection */\r
9156   if (connect(s, (struct sockaddr *) &sa,\r
9157               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9158     err = WSAGetLastError();\r
9159     WSACleanup();\r
9160     return err;\r
9161   }\r
9162 \r
9163   /* Prepare return value */\r
9164   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9165   cp->kind = CPSock;\r
9166   cp->sock = s;\r
9167   *pr = (ProcRef *) cp;\r
9168 \r
9169   return NO_ERROR;\r
9170 }\r
9171 \r
9172 int\r
9173 OpenCommPort(char *name, ProcRef *pr)\r
9174 {\r
9175   HANDLE h;\r
9176   COMMTIMEOUTS ct;\r
9177   ChildProc *cp;\r
9178   char fullname[MSG_SIZ];\r
9179 \r
9180   if (*name != '\\')\r
9181     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9182   else\r
9183     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9184 \r
9185   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9186                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9187   if (h == (HANDLE) -1) {\r
9188     return GetLastError();\r
9189   }\r
9190   hCommPort = h;\r
9191 \r
9192   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9193 \r
9194   /* Accumulate characters until a 100ms pause, then parse */\r
9195   ct.ReadIntervalTimeout = 100;\r
9196   ct.ReadTotalTimeoutMultiplier = 0;\r
9197   ct.ReadTotalTimeoutConstant = 0;\r
9198   ct.WriteTotalTimeoutMultiplier = 0;\r
9199   ct.WriteTotalTimeoutConstant = 0;\r
9200   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9201 \r
9202   /* Prepare return value */\r
9203   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9204   cp->kind = CPComm;\r
9205   cp->hFrom = h;\r
9206   cp->hTo = h;\r
9207   *pr = (ProcRef *) cp;\r
9208 \r
9209   return NO_ERROR;\r
9210 }\r
9211 \r
9212 int\r
9213 OpenLoopback(ProcRef *pr)\r
9214 {\r
9215   DisplayFatalError(_("Not implemented"), 0, 1);\r
9216   return NO_ERROR;\r
9217 }\r
9218 \r
9219 \r
9220 int\r
9221 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9222 {\r
9223   ChildProc *cp;\r
9224   int err;\r
9225   SOCKET s, s2, s3;\r
9226   struct sockaddr_in sa, mysa;\r
9227   struct hostent FAR *hp;\r
9228   unsigned short uport;\r
9229   WORD wVersionRequested;\r
9230   WSADATA wsaData;\r
9231   int fromPort;\r
9232   char stderrPortStr[MSG_SIZ];\r
9233 \r
9234   /* Initialize socket DLL */\r
9235   wVersionRequested = MAKEWORD(1, 1);\r
9236   err = WSAStartup(wVersionRequested, &wsaData);\r
9237   if (err != 0) return err;\r
9238 \r
9239   /* Resolve remote host name */\r
9240   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9241   if (!(hp = gethostbyname(host))) {\r
9242     unsigned int b0, b1, b2, b3;\r
9243 \r
9244     err = WSAGetLastError();\r
9245 \r
9246     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9247       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9248       hp->h_addrtype = AF_INET;\r
9249       hp->h_length = 4;\r
9250       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9251       hp->h_addr_list[0] = (char *) malloc(4);\r
9252       hp->h_addr_list[0][0] = (char) b0;\r
9253       hp->h_addr_list[0][1] = (char) b1;\r
9254       hp->h_addr_list[0][2] = (char) b2;\r
9255       hp->h_addr_list[0][3] = (char) b3;\r
9256     } else {\r
9257       WSACleanup();\r
9258       return err;\r
9259     }\r
9260   }\r
9261   sa.sin_family = hp->h_addrtype;\r
9262   uport = (unsigned short) 514;\r
9263   sa.sin_port = htons(uport);\r
9264   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9265 \r
9266   /* Bind local socket to unused "privileged" port address\r
9267    */\r
9268   s = INVALID_SOCKET;\r
9269   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9270   mysa.sin_family = AF_INET;\r
9271   mysa.sin_addr.s_addr = INADDR_ANY;\r
9272   for (fromPort = 1023;; fromPort--) {\r
9273     if (fromPort < 0) {\r
9274       WSACleanup();\r
9275       return WSAEADDRINUSE;\r
9276     }\r
9277     if (s == INVALID_SOCKET) {\r
9278       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9279         err = WSAGetLastError();\r
9280         WSACleanup();\r
9281         return err;\r
9282       }\r
9283     }\r
9284     uport = (unsigned short) fromPort;\r
9285     mysa.sin_port = htons(uport);\r
9286     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9287         == SOCKET_ERROR) {\r
9288       err = WSAGetLastError();\r
9289       if (err == WSAEADDRINUSE) continue;\r
9290       WSACleanup();\r
9291       return err;\r
9292     }\r
9293     if (connect(s, (struct sockaddr *) &sa,\r
9294       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9295       err = WSAGetLastError();\r
9296       if (err == WSAEADDRINUSE) {\r
9297         closesocket(s);\r
9298         s = -1;\r
9299         continue;\r
9300       }\r
9301       WSACleanup();\r
9302       return err;\r
9303     }\r
9304     break;\r
9305   }\r
9306 \r
9307   /* Bind stderr local socket to unused "privileged" port address\r
9308    */\r
9309   s2 = INVALID_SOCKET;\r
9310   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9311   mysa.sin_family = AF_INET;\r
9312   mysa.sin_addr.s_addr = INADDR_ANY;\r
9313   for (fromPort = 1023;; fromPort--) {\r
9314     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9315     if (fromPort < 0) {\r
9316       (void) closesocket(s);\r
9317       WSACleanup();\r
9318       return WSAEADDRINUSE;\r
9319     }\r
9320     if (s2 == INVALID_SOCKET) {\r
9321       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9322         err = WSAGetLastError();\r
9323         closesocket(s);\r
9324         WSACleanup();\r
9325         return err;\r
9326       }\r
9327     }\r
9328     uport = (unsigned short) fromPort;\r
9329     mysa.sin_port = htons(uport);\r
9330     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9331         == SOCKET_ERROR) {\r
9332       err = WSAGetLastError();\r
9333       if (err == WSAEADDRINUSE) continue;\r
9334       (void) closesocket(s);\r
9335       WSACleanup();\r
9336       return err;\r
9337     }\r
9338     if (listen(s2, 1) == SOCKET_ERROR) {\r
9339       err = WSAGetLastError();\r
9340       if (err == WSAEADDRINUSE) {\r
9341         closesocket(s2);\r
9342         s2 = INVALID_SOCKET;\r
9343         continue;\r
9344       }\r
9345       (void) closesocket(s);\r
9346       (void) closesocket(s2);\r
9347       WSACleanup();\r
9348       return err;\r
9349     }\r
9350     break;\r
9351   }\r
9352   prevStderrPort = fromPort; // remember port used\r
9353   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9354 \r
9355   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9356     err = WSAGetLastError();\r
9357     (void) closesocket(s);\r
9358     (void) closesocket(s2);\r
9359     WSACleanup();\r
9360     return err;\r
9361   }\r
9362 \r
9363   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9364     err = WSAGetLastError();\r
9365     (void) closesocket(s);\r
9366     (void) closesocket(s2);\r
9367     WSACleanup();\r
9368     return err;\r
9369   }\r
9370   if (*user == NULLCHAR) user = UserName();\r
9371   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9372     err = WSAGetLastError();\r
9373     (void) closesocket(s);\r
9374     (void) closesocket(s2);\r
9375     WSACleanup();\r
9376     return err;\r
9377   }\r
9378   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9379     err = WSAGetLastError();\r
9380     (void) closesocket(s);\r
9381     (void) closesocket(s2);\r
9382     WSACleanup();\r
9383     return err;\r
9384   }\r
9385 \r
9386   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9387     err = WSAGetLastError();\r
9388     (void) closesocket(s);\r
9389     (void) closesocket(s2);\r
9390     WSACleanup();\r
9391     return err;\r
9392   }\r
9393   (void) closesocket(s2);  /* Stop listening */\r
9394 \r
9395   /* Prepare return value */\r
9396   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9397   cp->kind = CPRcmd;\r
9398   cp->sock = s;\r
9399   cp->sock2 = s3;\r
9400   *pr = (ProcRef *) cp;\r
9401 \r
9402   return NO_ERROR;\r
9403 }\r
9404 \r
9405 \r
9406 InputSourceRef\r
9407 AddInputSource(ProcRef pr, int lineByLine,\r
9408                InputCallback func, VOIDSTAR closure)\r
9409 {\r
9410   InputSource *is, *is2 = NULL;\r
9411   ChildProc *cp = (ChildProc *) pr;\r
9412 \r
9413   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9414   is->lineByLine = lineByLine;\r
9415   is->func = func;\r
9416   is->closure = closure;\r
9417   is->second = NULL;\r
9418   is->next = is->buf;\r
9419   if (pr == NoProc) {\r
9420     is->kind = CPReal;\r
9421     consoleInputSource = is;\r
9422   } else {\r
9423     is->kind = cp->kind;\r
9424     /* \r
9425         [AS] Try to avoid a race condition if the thread is given control too early:\r
9426         we create all threads suspended so that the is->hThread variable can be\r
9427         safely assigned, then let the threads start with ResumeThread.\r
9428     */\r
9429     switch (cp->kind) {\r
9430     case CPReal:\r
9431       is->hFile = cp->hFrom;\r
9432       cp->hFrom = NULL; /* now owned by InputThread */\r
9433       is->hThread =\r
9434         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9435                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9436       break;\r
9437 \r
9438     case CPComm:\r
9439       is->hFile = cp->hFrom;\r
9440       cp->hFrom = NULL; /* now owned by InputThread */\r
9441       is->hThread =\r
9442         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9443                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9444       break;\r
9445 \r
9446     case CPSock:\r
9447       is->sock = cp->sock;\r
9448       is->hThread =\r
9449         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9450                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9451       break;\r
9452 \r
9453     case CPRcmd:\r
9454       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9455       *is2 = *is;\r
9456       is->sock = cp->sock;\r
9457       is->second = is2;\r
9458       is2->sock = cp->sock2;\r
9459       is2->second = is2;\r
9460       is->hThread =\r
9461         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9462                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9463       is2->hThread =\r
9464         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9465                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9466       break;\r
9467     }\r
9468 \r
9469     if( is->hThread != NULL ) {\r
9470         ResumeThread( is->hThread );\r
9471     }\r
9472 \r
9473     if( is2 != NULL && is2->hThread != NULL ) {\r
9474         ResumeThread( is2->hThread );\r
9475     }\r
9476   }\r
9477 \r
9478   return (InputSourceRef) is;\r
9479 }\r
9480 \r
9481 void\r
9482 RemoveInputSource(InputSourceRef isr)\r
9483 {\r
9484   InputSource *is;\r
9485 \r
9486   is = (InputSource *) isr;\r
9487   is->hThread = NULL;  /* tell thread to stop */\r
9488   CloseHandle(is->hThread);\r
9489   if (is->second != NULL) {\r
9490     is->second->hThread = NULL;\r
9491     CloseHandle(is->second->hThread);\r
9492   }\r
9493 }\r
9494 \r
9495 int no_wrap(char *message, int count)\r
9496 {\r
9497     ConsoleOutput(message, count, FALSE);\r
9498     return count;\r
9499 }\r
9500 \r
9501 int\r
9502 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9503 {\r
9504   DWORD dOutCount;\r
9505   int outCount = SOCKET_ERROR;\r
9506   ChildProc *cp = (ChildProc *) pr;\r
9507   static OVERLAPPED ovl;\r
9508   static int line = 0;\r
9509 \r
9510   if (pr == NoProc)\r
9511   {\r
9512     if (appData.noJoin || !appData.useInternalWrap)\r
9513       return no_wrap(message, count);\r
9514     else\r
9515     {\r
9516       int width = get_term_width();\r
9517       int len = wrap(NULL, message, count, width, &line);\r
9518       char *msg = malloc(len);\r
9519       int dbgchk;\r
9520 \r
9521       if (!msg)\r
9522         return no_wrap(message, count);\r
9523       else\r
9524       {\r
9525         dbgchk = wrap(msg, message, count, width, &line);\r
9526         if (dbgchk != len && appData.debugMode)\r
9527             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9528         ConsoleOutput(msg, len, FALSE);\r
9529         free(msg);\r
9530         return len;\r
9531       }\r
9532     }\r
9533   }\r
9534 \r
9535   if (ovl.hEvent == NULL) {\r
9536     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9537   }\r
9538   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9539 \r
9540   switch (cp->kind) {\r
9541   case CPSock:\r
9542   case CPRcmd:\r
9543     outCount = send(cp->sock, message, count, 0);\r
9544     if (outCount == SOCKET_ERROR) {\r
9545       *outError = WSAGetLastError();\r
9546     } else {\r
9547       *outError = NO_ERROR;\r
9548     }\r
9549     break;\r
9550 \r
9551   case CPReal:\r
9552     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9553                   &dOutCount, NULL)) {\r
9554       *outError = NO_ERROR;\r
9555       outCount = (int) dOutCount;\r
9556     } else {\r
9557       *outError = GetLastError();\r
9558     }\r
9559     break;\r
9560 \r
9561   case CPComm:\r
9562     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9563                             &dOutCount, &ovl);\r
9564     if (*outError == NO_ERROR) {\r
9565       outCount = (int) dOutCount;\r
9566     }\r
9567     break;\r
9568   }\r
9569   return outCount;\r
9570 }\r
9571 \r
9572 int\r
9573 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9574                        long msdelay)\r
9575 {\r
9576   /* Ignore delay, not implemented for WinBoard */\r
9577   return OutputToProcess(pr, message, count, outError);\r
9578 }\r
9579 \r
9580 \r
9581 void\r
9582 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9583                         char *buf, int count, int error)\r
9584 {\r
9585   DisplayFatalError(_("Not implemented"), 0, 1);\r
9586 }\r
9587 \r
9588 /* see wgamelist.c for Game List functions */\r
9589 /* see wedittags.c for Edit Tags functions */\r
9590 \r
9591 \r
9592 VOID\r
9593 ICSInitScript()\r
9594 {\r
9595   FILE *f;\r
9596   char buf[MSG_SIZ];\r
9597   char *dummy;\r
9598 \r
9599   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9600     f = fopen(buf, "r");\r
9601     if (f != NULL) {\r
9602       ProcessICSInitScript(f);\r
9603       fclose(f);\r
9604     }\r
9605   }\r
9606 }\r
9607 \r
9608 \r
9609 VOID\r
9610 StartAnalysisClock()\r
9611 {\r
9612   if (analysisTimerEvent) return;\r
9613   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9614                                         (UINT) 2000, NULL);\r
9615 }\r
9616 \r
9617 VOID\r
9618 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9619 {\r
9620   highlightInfo.sq[0].x = fromX;\r
9621   highlightInfo.sq[0].y = fromY;\r
9622   highlightInfo.sq[1].x = toX;\r
9623   highlightInfo.sq[1].y = toY;\r
9624 }\r
9625 \r
9626 VOID\r
9627 ClearHighlights()\r
9628 {\r
9629   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9630     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9631 }\r
9632 \r
9633 VOID\r
9634 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9635 {\r
9636   premoveHighlightInfo.sq[0].x = fromX;\r
9637   premoveHighlightInfo.sq[0].y = fromY;\r
9638   premoveHighlightInfo.sq[1].x = toX;\r
9639   premoveHighlightInfo.sq[1].y = toY;\r
9640 }\r
9641 \r
9642 VOID\r
9643 ClearPremoveHighlights()\r
9644 {\r
9645   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9646     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9647 }\r
9648 \r
9649 VOID\r
9650 ShutDownFrontEnd()\r
9651 {\r
9652   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9653   DeleteClipboardTempFiles();\r
9654 }\r
9655 \r
9656 void\r
9657 BoardToTop()\r
9658 {\r
9659     if (IsIconic(hwndMain))\r
9660       ShowWindow(hwndMain, SW_RESTORE);\r
9661 \r
9662     SetActiveWindow(hwndMain);\r
9663 }\r
9664 \r
9665 /*\r
9666  * Prototypes for animation support routines\r
9667  */\r
9668 static void ScreenSquare(int column, int row, POINT * pt);\r
9669 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9670      POINT frames[], int * nFrames);\r
9671 \r
9672 \r
9673 #define kFactor 4\r
9674 \r
9675 void\r
9676 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9677 {       // [HGM] atomic: animate blast wave\r
9678         int i;\r
9679 \r
9680         explodeInfo.fromX = fromX;\r
9681         explodeInfo.fromY = fromY;\r
9682         explodeInfo.toX = toX;\r
9683         explodeInfo.toY = toY;\r
9684         for(i=1; i<4*kFactor; i++) {\r
9685             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9686             DrawPosition(FALSE, board);\r
9687             Sleep(appData.animSpeed);\r
9688         }\r
9689         explodeInfo.radius = 0;\r
9690         DrawPosition(TRUE, board);\r
9691 }\r
9692 \r
9693 void\r
9694 AnimateMove(board, fromX, fromY, toX, toY)\r
9695      Board board;\r
9696      int fromX;\r
9697      int fromY;\r
9698      int toX;\r
9699      int toY;\r
9700 {\r
9701   ChessSquare piece;\r
9702   POINT start, finish, mid;\r
9703   POINT frames[kFactor * 2 + 1];\r
9704   int nFrames, n;\r
9705 \r
9706   if (!appData.animate) return;\r
9707   if (doingSizing) return;\r
9708   if (fromY < 0 || fromX < 0) return;\r
9709   piece = board[fromY][fromX];\r
9710   if (piece >= EmptySquare) return;\r
9711 \r
9712   ScreenSquare(fromX, fromY, &start);\r
9713   ScreenSquare(toX, toY, &finish);\r
9714 \r
9715   /* All moves except knight jumps move in straight line */\r
9716   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9717     mid.x = start.x + (finish.x - start.x) / 2;\r
9718     mid.y = start.y + (finish.y - start.y) / 2;\r
9719   } else {\r
9720     /* Knight: make straight movement then diagonal */\r
9721     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9722        mid.x = start.x + (finish.x - start.x) / 2;\r
9723        mid.y = start.y;\r
9724      } else {\r
9725        mid.x = start.x;\r
9726        mid.y = start.y + (finish.y - start.y) / 2;\r
9727      }\r
9728   }\r
9729   \r
9730   /* Don't use as many frames for very short moves */\r
9731   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9732     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9733   else\r
9734     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9735 \r
9736   animInfo.from.x = fromX;\r
9737   animInfo.from.y = fromY;\r
9738   animInfo.to.x = toX;\r
9739   animInfo.to.y = toY;\r
9740   animInfo.lastpos = start;\r
9741   animInfo.piece = piece;\r
9742   for (n = 0; n < nFrames; n++) {\r
9743     animInfo.pos = frames[n];\r
9744     DrawPosition(FALSE, NULL);\r
9745     animInfo.lastpos = animInfo.pos;\r
9746     Sleep(appData.animSpeed);\r
9747   }\r
9748   animInfo.pos = finish;\r
9749   DrawPosition(FALSE, NULL);\r
9750   animInfo.piece = EmptySquare;\r
9751   Explode(board, fromX, fromY, toX, toY);\r
9752 }\r
9753 \r
9754 /*      Convert board position to corner of screen rect and color       */\r
9755 \r
9756 static void\r
9757 ScreenSquare(column, row, pt)\r
9758      int column; int row; POINT * pt;\r
9759 {\r
9760   if (flipView) {\r
9761     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9762     pt->y = lineGap + row * (squareSize + lineGap);\r
9763   } else {\r
9764     pt->x = lineGap + column * (squareSize + lineGap);\r
9765     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9766   }\r
9767 }\r
9768 \r
9769 /*      Generate a series of frame coords from start->mid->finish.\r
9770         The movement rate doubles until the half way point is\r
9771         reached, then halves back down to the final destination,\r
9772         which gives a nice slow in/out effect. The algorithmn\r
9773         may seem to generate too many intermediates for short\r
9774         moves, but remember that the purpose is to attract the\r
9775         viewers attention to the piece about to be moved and\r
9776         then to where it ends up. Too few frames would be less\r
9777         noticeable.                                             */\r
9778 \r
9779 static void\r
9780 Tween(start, mid, finish, factor, frames, nFrames)\r
9781      POINT * start; POINT * mid;\r
9782      POINT * finish; int factor;\r
9783      POINT frames[]; int * nFrames;\r
9784 {\r
9785   int n, fraction = 1, count = 0;\r
9786 \r
9787   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9788   for (n = 0; n < factor; n++)\r
9789     fraction *= 2;\r
9790   for (n = 0; n < factor; n++) {\r
9791     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9792     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9793     count ++;\r
9794     fraction = fraction / 2;\r
9795   }\r
9796   \r
9797   /* Midpoint */\r
9798   frames[count] = *mid;\r
9799   count ++;\r
9800   \r
9801   /* Slow out, stepping 1/2, then 1/4, ... */\r
9802   fraction = 2;\r
9803   for (n = 0; n < factor; n++) {\r
9804     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9805     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9806     count ++;\r
9807     fraction = fraction * 2;\r
9808   }\r
9809   *nFrames = count;\r
9810 }\r
9811 \r
9812 void\r
9813 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9814 {\r
9815     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9816 \r
9817     EvalGraphSet( first, last, current, pvInfoList );\r
9818 }\r
9819 \r
9820 void\r
9821 SettingsPopUp(ChessProgramState *cps)\r
9822 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9823       EngineOptionsPopup(savedHwnd, cps);\r
9824 }\r