87b1830023f6cdc4cc66b1d4bdedeb43225ad472
[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 #include <io.h>\r
77 \r
78 #if __GNUC__\r
79 #include <errno.h>\r
80 #include <string.h>\r
81 #endif\r
82 \r
83 #include "common.h"\r
84 #include "frontend.h"\r
85 #include "backend.h"\r
86 #include "winboard.h"\r
87 #include "moves.h"\r
88 #include "wclipbrd.h"\r
89 #include "woptions.h"\r
90 #include "wsockerr.h"\r
91 #include "defaults.h"\r
92 #include "help.h"\r
93 #include "wsnap.h"\r
94 \r
95 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
96 \r
97   int myrandom(void);\r
98   void mysrandom(unsigned int seed);\r
99 \r
100 extern int whiteFlag, blackFlag;\r
101 Boolean flipClock = FALSE;\r
102 extern HANDLE chatHandle[];\r
103 extern int ics_type;\r
104 \r
105 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
106 VOID NewVariantPopup(HWND hwnd);\r
107 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
108                    /*char*/int promoChar));\r
109 void DisplayMove P((int moveNumber));\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
111 void ChatPopUp P((char *s));\r
112 typedef struct {\r
113   ChessSquare piece;  \r
114   POINT pos;      /* window coordinates of current pos */\r
115   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
116   POINT from;     /* board coordinates of the piece's orig pos */\r
117   POINT to;       /* board coordinates of the piece's new pos */\r
118 } AnimInfo;\r
119 \r
120 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
121 \r
122 typedef struct {\r
123   POINT start;    /* window coordinates of start pos */\r
124   POINT pos;      /* window coordinates of current pos */\r
125   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
126   POINT from;     /* board coordinates of the piece's orig pos */\r
127   ChessSquare piece;\r
128 } DragInfo;\r
129 \r
130 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
131 \r
132 typedef struct {\r
133   POINT sq[2];    /* board coordinates of from, to squares */\r
134 } HighlightInfo;\r
135 \r
136 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
137 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
138 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
139 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
140 \r
141 typedef struct { // [HGM] atomic\r
142   int fromX, fromY, toX, toY, radius;\r
143 } ExplodeInfo;\r
144 \r
145 static ExplodeInfo explodeInfo;\r
146 \r
147 /* Window class names */\r
148 char szAppName[] = "WinBoard";\r
149 char szConsoleName[] = "WBConsole";\r
150 \r
151 /* Title bar text */\r
152 char szTitle[] = "WinBoard";\r
153 char szConsoleTitle[] = "I C S Interaction";\r
154 \r
155 char *programName;\r
156 char *settingsFileName;\r
157 Boolean saveSettingsOnExit;\r
158 char installDir[MSG_SIZ];\r
159 int errorExitStatus;\r
160 \r
161 BoardSize boardSize;\r
162 Boolean chessProgram;\r
163 //static int boardX, boardY;\r
164 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
165 int squareSize, lineGap, minorSize;\r
166 static int winW, winH;\r
167 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
168 static int logoHeight = 0;\r
169 static char messageText[MESSAGE_TEXT_MAX];\r
170 static int clockTimerEvent = 0;\r
171 static int loadGameTimerEvent = 0;\r
172 static int analysisTimerEvent = 0;\r
173 static DelayedEventCallback delayedTimerCallback;\r
174 static int delayedTimerEvent = 0;\r
175 static int buttonCount = 2;\r
176 char *icsTextMenuString;\r
177 char *icsNames;\r
178 char *firstChessProgramNames;\r
179 char *secondChessProgramNames;\r
180 \r
181 #define PALETTESIZE 256\r
182 \r
183 HINSTANCE hInst;          /* current instance */\r
184 Boolean alwaysOnTop = FALSE;\r
185 RECT boardRect;\r
186 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
187   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
188 HPALETTE hPal;\r
189 ColorClass currentColorClass;\r
190 \r
191 static HWND savedHwnd;\r
192 HWND hCommPort = NULL;    /* currently open comm port */\r
193 static HWND hwndPause;    /* pause button */\r
194 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
195 static HBRUSH lightSquareBrush, darkSquareBrush,\r
196   blackSquareBrush, /* [HGM] for band between board and holdings */\r
197   explodeBrush,     /* [HGM] atomic */\r
198   markerBrush,      /* [HGM] markers */\r
199   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
200 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
201 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
202 static HPEN gridPen = NULL;\r
203 static HPEN highlightPen = NULL;\r
204 static HPEN premovePen = NULL;\r
205 static NPLOGPALETTE pLogPal;\r
206 static BOOL paletteChanged = FALSE;\r
207 static HICON iconWhite, iconBlack, iconCurrent;\r
208 static int doingSizing = FALSE;\r
209 static int lastSizing = 0;\r
210 static int prevStderrPort;\r
211 static HBITMAP userLogo;\r
212 \r
213 static HBITMAP liteBackTexture = NULL;\r
214 static HBITMAP darkBackTexture = NULL;\r
215 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
216 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
217 static int backTextureSquareSize = 0;\r
218 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
219 \r
220 #if __GNUC__ && !defined(_winmajor)\r
221 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
222 #else\r
223 #if defined(_winmajor)\r
224 #define oldDialog (_winmajor < 4)\r
225 #else\r
226 #define oldDialog 0\r
227 #endif\r
228 #endif\r
229 \r
230 #define INTERNATIONAL\r
231 \r
232 #ifdef INTERNATIONAL\r
233 #  define _(s) T_(s)\r
234 #  define N_(s) s\r
235 #else\r
236 #  define _(s) s\r
237 #  define N_(s) s\r
238 #  define T_(s) s\r
239 #  define Translate(x, y)\r
240 #  define LoadLanguageFile(s)\r
241 #endif\r
242 \r
243 #ifdef INTERNATIONAL\r
244 \r
245 Boolean barbaric; // flag indicating if translation is needed\r
246 \r
247 // list of item numbers used in each dialog (used to alter language at run time)\r
248 \r
249 #define ABOUTBOX -1  /* not sure why these are needed */\r
250 #define ABOUTBOX2 -1\r
251 \r
252 int dialogItems[][40] = {\r
253 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
254 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
255   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
256 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, IDOK, IDCANCEL }, \r
257 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
258   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
259 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
260 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
261   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
262 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
263 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
264   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
265 { ABOUTBOX2, IDC_ChessBoard }, \r
266 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
267   OPT_GameListClose, IDC_GameListDoFilter }, \r
268 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
269 { DLG_Error, IDOK }, \r
270 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
271   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
272 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
273 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
274   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
275   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
276 { DLG_IndexNumber, IDC_Index }, \r
277 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
278 { DLG_TypeInName, IDOK, IDCANCEL }, \r
279 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
280   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
281 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
282   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
283   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
284   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
285   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
286   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
287   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
288 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
289   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
290   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
291   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
292   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
293   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
294   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
295   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
296   GPB_General, GPB_Alarm }, \r
297 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
298   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
299   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
300   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
301   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
302   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
303   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
304   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size }, \r
305 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
306   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
307   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
308   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
309   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
310   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
311   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
312   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
313   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
314 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
315   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
316   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,\r
317   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
318 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
319 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
320   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
321 { DLG_MoveHistory }, \r
322 { DLG_EvalGraph }, \r
323 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
324 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
325 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
326   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
327   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
328   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
329 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
330   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
331   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
332 { 0 }\r
333 };\r
334 \r
335 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
336 static int lastChecked;\r
337 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
338 extern int tinyLayout;\r
339 extern char * menuBarText[][10];\r
340 \r
341 void\r
342 LoadLanguageFile(char *name)\r
343 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
344     FILE *f;\r
345     int i=0, j=0, n=0, k;\r
346     char buf[MSG_SIZ];\r
347 \r
348     if(!name || name[0] == NULLCHAR) return;\r
349       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
350     appData.language = oldLanguage;\r
351     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
352     if((f = fopen(buf, "r")) == NULL) return;\r
353     while((k = fgetc(f)) != EOF) {\r
354         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
355         languageBuf[i] = k;\r
356         if(k == '\n') {\r
357             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
358                 char *p;\r
359                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
360                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
361                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
362                         english[j] = languageBuf + n + 1; *p = 0;\r
363                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
364 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
365                     }\r
366                 }\r
367             }\r
368             n = i + 1;\r
369         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
370             switch(k) {\r
371               case 'n': k = '\n'; break;\r
372               case 'r': k = '\r'; break;\r
373               case 't': k = '\t'; break;\r
374             }\r
375             languageBuf[--i] = k;\r
376         }\r
377         i++;\r
378     }\r
379     fclose(f);\r
380     barbaric = (j != 0);\r
381     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
382 }\r
383 \r
384 char *\r
385 T_(char *s)\r
386 {   // return the translation of the given string\r
387     // efficiency can be improved a lot...\r
388     int i=0;\r
389 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
390     if(!barbaric) return s;\r
391     if(!s) return ""; // sanity\r
392     while(english[i]) {\r
393         if(!strcmp(s, english[i])) return foreign[i];\r
394         i++;\r
395     }\r
396     return s;\r
397 }\r
398 \r
399 void\r
400 Translate(HWND hDlg, int dialogID)\r
401 {   // translate all text items in the given dialog\r
402     int i=0, j, k;\r
403     char buf[MSG_SIZ], *s;\r
404     if(!barbaric) return;\r
405     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
406     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
407     GetWindowText( hDlg, buf, MSG_SIZ );\r
408     s = T_(buf);\r
409     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
410     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
411         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
412         if(strlen(buf) == 0) continue;\r
413         s = T_(buf);\r
414         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
415     }\r
416 }\r
417 \r
418 HMENU\r
419 TranslateOneMenu(int i, HMENU subMenu)\r
420 {\r
421     int j;\r
422     static MENUITEMINFO info;\r
423 \r
424     info.cbSize = sizeof(MENUITEMINFO);\r
425     info.fMask = MIIM_STATE | MIIM_TYPE;\r
426           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
427             char buf[MSG_SIZ];\r
428             info.dwTypeData = buf;\r
429             info.cch = sizeof(buf);\r
430             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
431             if(i < 10) {
432                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
433                 else menuText[i][j] = strdup(buf); // remember original on first change\r
434             }\r
435             if(buf[0] == NULLCHAR) continue;\r
436             info.dwTypeData = T_(buf);\r
437             info.cch = strlen(buf)+1;\r
438             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
439           }\r
440     return subMenu;\r
441 }\r
442 \r
443 void\r
444 TranslateMenus(int addLanguage)\r
445 {\r
446     int i;\r
447     WIN32_FIND_DATA fileData;\r
448     HANDLE hFind;\r
449 #define IDM_English 1970\r
450     if(1) {\r
451         HMENU mainMenu = GetMenu(hwndMain);\r
452         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
453           HMENU subMenu = GetSubMenu(mainMenu, i);\r
454           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
455                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
456           TranslateOneMenu(i, subMenu);\r
457         }\r
458         DrawMenuBar(hwndMain);\r
459     }\r
460 \r
461     if(!addLanguage) return;\r
462     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
463         HMENU mainMenu = GetMenu(hwndMain);\r
464         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
465         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
466         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
467         i = 0; lastChecked = IDM_English;\r
468         do {\r
469             char *p, *q = fileData.cFileName;\r
470             int checkFlag = MF_UNCHECKED;\r
471             languageFile[i] = strdup(q);\r
472             if(barbaric && !strcmp(oldLanguage, q)) {\r
473                 checkFlag = MF_CHECKED;\r
474                 lastChecked = IDM_English + i + 1;\r
475                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
476             }\r
477             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
478             p = strstr(fileData.cFileName, ".lng");\r
479             if(p) *p = 0;\r
480             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
481         } while(FindNextFile(hFind, &fileData));\r
482         FindClose(hFind);\r
483     }\r
484 }\r
485 \r
486 #endif\r
487 \r
488 typedef struct {\r
489   char *name;\r
490   int squareSize;\r
491   int lineGap;\r
492   int smallLayout;\r
493   int tinyLayout;\r
494   int cliWidth, cliHeight;\r
495 } SizeInfo;\r
496 \r
497 SizeInfo sizeInfo[] = \r
498 {\r
499   { "tiny",     21, 0, 1, 1, 0, 0 },\r
500   { "teeny",    25, 1, 1, 1, 0, 0 },\r
501   { "dinky",    29, 1, 1, 1, 0, 0 },\r
502   { "petite",   33, 1, 1, 1, 0, 0 },\r
503   { "slim",     37, 2, 1, 0, 0, 0 },\r
504   { "small",    40, 2, 1, 0, 0, 0 },\r
505   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
506   { "middling", 49, 2, 0, 0, 0, 0 },\r
507   { "average",  54, 2, 0, 0, 0, 0 },\r
508   { "moderate", 58, 3, 0, 0, 0, 0 },\r
509   { "medium",   64, 3, 0, 0, 0, 0 },\r
510   { "bulky",    72, 3, 0, 0, 0, 0 },\r
511   { "large",    80, 3, 0, 0, 0, 0 },\r
512   { "big",      87, 3, 0, 0, 0, 0 },\r
513   { "huge",     95, 3, 0, 0, 0, 0 },\r
514   { "giant",    108, 3, 0, 0, 0, 0 },\r
515   { "colossal", 116, 4, 0, 0, 0, 0 },\r
516   { "titanic",  129, 4, 0, 0, 0, 0 },\r
517   { NULL, 0, 0, 0, 0, 0, 0 }\r
518 };\r
519 \r
520 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
521 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
522 {\r
523   { 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
524   { 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
525   { 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
526   { 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
527   { 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
528   { 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
529   { 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
530   { 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
531   { 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
532   { 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
533   { 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
534   { 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
535   { 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
536   { 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
537   { 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
538   { 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
539   { 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
540   { 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
541 };\r
542 \r
543 MyFont *font[NUM_SIZES][NUM_FONTS];\r
544 \r
545 typedef struct {\r
546   char *label;\r
547   int id;\r
548   HWND hwnd;\r
549   WNDPROC wndproc;\r
550 } MyButtonDesc;\r
551 \r
552 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
553 #define N_BUTTONS 5\r
554 \r
555 MyButtonDesc buttonDesc[N_BUTTONS] =\r
556 {\r
557   {"<<", IDM_ToStart, NULL, NULL},\r
558   {"<", IDM_Backward, NULL, NULL},\r
559   {"P", IDM_Pause, NULL, NULL},\r
560   {">", IDM_Forward, NULL, NULL},\r
561   {">>", IDM_ToEnd, NULL, NULL},\r
562 };\r
563 \r
564 int tinyLayout = 0, smallLayout = 0;\r
565 #define MENU_BAR_ITEMS 9\r
566 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
567   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
568   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
569 };\r
570 \r
571 \r
572 MySound sounds[(int)NSoundClasses];\r
573 MyTextAttribs textAttribs[(int)NColorClasses];\r
574 \r
575 MyColorizeAttribs colorizeAttribs[] = {\r
576   { (COLORREF)0, 0, N_("Shout Text") },\r
577   { (COLORREF)0, 0, N_("SShout/CShout") },\r
578   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
579   { (COLORREF)0, 0, N_("Channel Text") },\r
580   { (COLORREF)0, 0, N_("Kibitz Text") },\r
581   { (COLORREF)0, 0, N_("Tell Text") },\r
582   { (COLORREF)0, 0, N_("Challenge Text") },\r
583   { (COLORREF)0, 0, N_("Request Text") },\r
584   { (COLORREF)0, 0, N_("Seek Text") },\r
585   { (COLORREF)0, 0, N_("Normal Text") },\r
586   { (COLORREF)0, 0, N_("None") }\r
587 };\r
588 \r
589 \r
590 \r
591 static char *commentTitle;\r
592 static char *commentText;\r
593 static int commentIndex;\r
594 static Boolean editComment = FALSE;\r
595 \r
596 \r
597 char errorTitle[MSG_SIZ];\r
598 char errorMessage[2*MSG_SIZ];\r
599 HWND errorDialog = NULL;\r
600 BOOLEAN moveErrorMessageUp = FALSE;\r
601 BOOLEAN consoleEcho = TRUE;\r
602 CHARFORMAT consoleCF;\r
603 COLORREF consoleBackgroundColor;\r
604 \r
605 char *programVersion;\r
606 \r
607 #define CPReal 1\r
608 #define CPComm 2\r
609 #define CPSock 3\r
610 #define CPRcmd 4\r
611 typedef int CPKind;\r
612 \r
613 typedef struct {\r
614   CPKind kind;\r
615   HANDLE hProcess;\r
616   DWORD pid;\r
617   HANDLE hTo;\r
618   HANDLE hFrom;\r
619   SOCKET sock;\r
620   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
621 } ChildProc;\r
622 \r
623 #define INPUT_SOURCE_BUF_SIZE 4096\r
624 \r
625 typedef struct _InputSource {\r
626   CPKind kind;\r
627   HANDLE hFile;\r
628   SOCKET sock;\r
629   int lineByLine;\r
630   HANDLE hThread;\r
631   DWORD id;\r
632   char buf[INPUT_SOURCE_BUF_SIZE];\r
633   char *next;\r
634   DWORD count;\r
635   int error;\r
636   InputCallback func;\r
637   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
638   VOIDSTAR closure;\r
639 } InputSource;\r
640 \r
641 InputSource *consoleInputSource;\r
642 \r
643 DCB dcb;\r
644 \r
645 /* forward */\r
646 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
647 VOID ConsoleCreate();\r
648 LRESULT CALLBACK\r
649   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
650 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
651 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
652 VOID ParseCommSettings(char *arg, DCB *dcb);\r
653 LRESULT CALLBACK\r
654   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
655 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
656 void ParseIcsTextMenu(char *icsTextMenuString);\r
657 VOID PopUpNameDialog(char firstchar);\r
658 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
659 \r
660 /* [AS] */\r
661 int NewGameFRC();\r
662 int GameListOptions();\r
663 \r
664 int dummy; // [HGM] for obsolete args\r
665 \r
666 HWND hwndMain = NULL;        /* root window*/\r
667 HWND hwndConsole = NULL;\r
668 HWND commentDialog = NULL;\r
669 HWND moveHistoryDialog = NULL;\r
670 HWND evalGraphDialog = NULL;\r
671 HWND engineOutputDialog = NULL;\r
672 HWND gameListDialog = NULL;\r
673 HWND editTagsDialog = NULL;\r
674 \r
675 int commentUp = FALSE;\r
676 \r
677 WindowPlacement wpMain;\r
678 WindowPlacement wpConsole;\r
679 WindowPlacement wpComment;\r
680 WindowPlacement wpMoveHistory;\r
681 WindowPlacement wpEvalGraph;\r
682 WindowPlacement wpEngineOutput;\r
683 WindowPlacement wpGameList;\r
684 WindowPlacement wpTags;\r
685 \r
686 VOID EngineOptionsPopup(); // [HGM] settings\r
687 \r
688 VOID GothicPopUp(char *title, VariantClass variant);\r
689 /*\r
690  * Setting "frozen" should disable all user input other than deleting\r
691  * the window.  We do this while engines are initializing themselves.\r
692  */\r
693 static int frozen = 0;\r
694 static int oldMenuItemState[MENU_BAR_ITEMS];\r
695 void FreezeUI()\r
696 {\r
697   HMENU hmenu;\r
698   int i;\r
699 \r
700   if (frozen) return;\r
701   frozen = 1;\r
702   hmenu = GetMenu(hwndMain);\r
703   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
704     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
705   }\r
706   DrawMenuBar(hwndMain);\r
707 }\r
708 \r
709 /* Undo a FreezeUI */\r
710 void ThawUI()\r
711 {\r
712   HMENU hmenu;\r
713   int i;\r
714 \r
715   if (!frozen) return;\r
716   frozen = 0;\r
717   hmenu = GetMenu(hwndMain);\r
718   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
719     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
720   }\r
721   DrawMenuBar(hwndMain);\r
722 }\r
723 \r
724 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
725 \r
726 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
727 #ifdef JAWS\r
728 #include "jaws.c"\r
729 #else\r
730 #define JAWS_INIT\r
731 #define JAWS_ARGS\r
732 #define JAWS_ALT_INTERCEPT\r
733 #define JAWS_KB_NAVIGATION\r
734 #define JAWS_MENU_ITEMS\r
735 #define JAWS_SILENCE\r
736 #define JAWS_REPLAY\r
737 #define JAWS_ACCEL\r
738 #define JAWS_COPYRIGHT\r
739 #define JAWS_DELETE(X) X\r
740 #define SAYMACHINEMOVE()\r
741 #define SAY(X)\r
742 #endif\r
743 \r
744 /*---------------------------------------------------------------------------*\\r
745  *\r
746  * WinMain\r
747  *\r
748 \*---------------------------------------------------------------------------*/\r
749 \r
750 int APIENTRY\r
751 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
752         LPSTR lpCmdLine, int nCmdShow)\r
753 {\r
754   MSG msg;\r
755   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
756 //  INITCOMMONCONTROLSEX ex;\r
757 \r
758   debugFP = stderr;\r
759 \r
760   LoadLibrary("RICHED32.DLL");\r
761   consoleCF.cbSize = sizeof(CHARFORMAT);\r
762 \r
763   if (!InitApplication(hInstance)) {\r
764     return (FALSE);\r
765   }\r
766   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
767     return (FALSE);\r
768   }\r
769 \r
770   JAWS_INIT\r
771   TranslateMenus(1);\r
772 \r
773 //  InitCommonControlsEx(&ex);\r
774   InitCommonControls();\r
775 \r
776   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
777   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
778   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
779 \r
780   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
781 \r
782   while (GetMessage(&msg, /* message structure */\r
783                     NULL, /* handle of window receiving the message */\r
784                     0,    /* lowest message to examine */\r
785                     0))   /* highest message to examine */\r
786     {\r
787 \r
788       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
789         // [HGM] navigate: switch between all windows with tab\r
790         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
791         int i, currentElement = 0;\r
792 \r
793         // first determine what element of the chain we come from (if any)\r
794         if(appData.icsActive) {\r
795             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
796             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
797         }\r
798         if(engineOutputDialog && EngineOutputIsUp()) {\r
799             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
800             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
801         }\r
802         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
803             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
804         }\r
805         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
806         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
807         if(msg.hwnd == e1)                 currentElement = 2; else\r
808         if(msg.hwnd == e2)                 currentElement = 3; else\r
809         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
810         if(msg.hwnd == mh)                currentElement = 4; else\r
811         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
812         if(msg.hwnd == hText)  currentElement = 5; else\r
813         if(msg.hwnd == hInput) currentElement = 6; else\r
814         for (i = 0; i < N_BUTTONS; i++) {\r
815             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
816         }\r
817 \r
818         // determine where to go to\r
819         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
820           do {\r
821             currentElement = (currentElement + direction) % 7;\r
822             switch(currentElement) {\r
823                 case 0:\r
824                   h = hwndMain; break; // passing this case always makes the loop exit\r
825                 case 1:\r
826                   h = buttonDesc[0].hwnd; break; // could be NULL\r
827                 case 2:\r
828                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
829                   h = e1; break;\r
830                 case 3:\r
831                   if(!EngineOutputIsUp()) continue;\r
832                   h = e2; break;\r
833                 case 4:\r
834                   if(!MoveHistoryIsUp()) continue;\r
835                   h = mh; break;\r
836 //              case 6: // input to eval graph does not seem to get here!\r
837 //                if(!EvalGraphIsUp()) continue;\r
838 //                h = evalGraphDialog; break;\r
839                 case 5:\r
840                   if(!appData.icsActive) continue;\r
841                   SAY("display");\r
842                   h = hText; break;\r
843                 case 6:\r
844                   if(!appData.icsActive) continue;\r
845                   SAY("input");\r
846                   h = hInput; break;\r
847             }\r
848           } while(h == 0);\r
849 \r
850           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
851           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
852           SetFocus(h);\r
853 \r
854           continue; // this message now has been processed\r
855         }\r
856       }\r
857 \r
858       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
859           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
860           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
861           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
862           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
863           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
864           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
865           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
866           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
867           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
868         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
869         for(i=0; i<MAX_CHAT; i++) \r
870             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
871                 done = 1; break;\r
872         }\r
873         if(done) continue; // [HGM] chat: end patch\r
874         TranslateMessage(&msg); /* Translates virtual key codes */\r
875         DispatchMessage(&msg);  /* Dispatches message to window */\r
876       }\r
877     }\r
878 \r
879 \r
880   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
881 }\r
882 \r
883 /*---------------------------------------------------------------------------*\\r
884  *\r
885  * Initialization functions\r
886  *\r
887 \*---------------------------------------------------------------------------*/\r
888 \r
889 void\r
890 SetUserLogo()\r
891 {   // update user logo if necessary\r
892     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
893 \r
894     if(appData.autoLogo) {\r
895           curName = UserName();\r
896           if(strcmp(curName, oldUserName)) {\r
897                 GetCurrentDirectory(MSG_SIZ, dir);\r
898                 SetCurrentDirectory(installDir);\r
899                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
900                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
901                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
902                 if(userLogo == NULL)\r
903                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
904                 SetCurrentDirectory(dir); /* return to prev directory */\r
905           }\r
906     }\r
907 }\r
908 \r
909 BOOL\r
910 InitApplication(HINSTANCE hInstance)\r
911 {\r
912   WNDCLASS wc;\r
913 \r
914   /* Fill in window class structure with parameters that describe the */\r
915   /* main window. */\r
916 \r
917   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
918   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
919   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
920   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
921   wc.hInstance     = hInstance;         /* Owner of this class */\r
922   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
923   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
924   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
925   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
926   wc.lpszClassName = szAppName;                 /* Name to register as */\r
927 \r
928   /* Register the window class and return success/failure code. */\r
929   if (!RegisterClass(&wc)) return FALSE;\r
930 \r
931   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
932   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
933   wc.cbClsExtra    = 0;\r
934   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
935   wc.hInstance     = hInstance;\r
936   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
937   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
938   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
939   wc.lpszMenuName  = NULL;\r
940   wc.lpszClassName = szConsoleName;\r
941 \r
942   if (!RegisterClass(&wc)) return FALSE;\r
943   return TRUE;\r
944 }\r
945 \r
946 \r
947 /* Set by InitInstance, used by EnsureOnScreen */\r
948 int screenHeight, screenWidth;\r
949 \r
950 void\r
951 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
952 {\r
953 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
954   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
955   if (*x > screenWidth - 32) *x = 0;\r
956   if (*y > screenHeight - 32) *y = 0;\r
957   if (*x < minX) *x = minX;\r
958   if (*y < minY) *y = minY;\r
959 }\r
960 \r
961 VOID\r
962 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
963 {\r
964   char buf[MSG_SIZ], dir[MSG_SIZ];\r
965   GetCurrentDirectory(MSG_SIZ, dir);\r
966   SetCurrentDirectory(installDir);\r
967   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
968       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
969 \r
970       if (cps->programLogo == NULL && appData.debugMode) {\r
971           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
972       }\r
973   } else if(appData.autoLogo) {\r
974       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
975         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
976         cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
977       } else\r
978       if(appData.directory[n] && appData.directory[n][0]) {\r
979         SetCurrentDirectory(appData.directory[n]);\r
980         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
981       }\r
982   }\r
983   SetCurrentDirectory(dir); /* return to prev directory */\r
984 }\r
985 \r
986 BOOL\r
987 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
988 {\r
989   HWND hwnd; /* Main window handle. */\r
990   int ibs;\r
991   WINDOWPLACEMENT wp;\r
992   char *filepart;\r
993 \r
994   hInst = hInstance;    /* Store instance handle in our global variable */\r
995   programName = szAppName;\r
996 \r
997   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
998     *filepart = NULLCHAR;\r
999   } else {\r
1000     GetCurrentDirectory(MSG_SIZ, installDir);\r
1001   }\r
1002   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1003   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
1004   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1005   /* xboard, and older WinBoards, controlled the move sound with the\r
1006      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1007      always turn the option on (so that the backend will call us),\r
1008      then let the user turn the sound off by setting it to silence if\r
1009      desired.  To accommodate old winboard.ini files saved by old\r
1010      versions of WinBoard, we also turn off the sound if the option\r
1011      was initially set to false. [HGM] taken out of InitAppData */\r
1012   if (!appData.ringBellAfterMoves) {\r
1013     sounds[(int)SoundMove].name = strdup("");\r
1014     appData.ringBellAfterMoves = TRUE;\r
1015   }\r
1016   if (appData.debugMode) {\r
1017     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1018     setbuf(debugFP, NULL);\r
1019   }\r
1020 \r
1021   LoadLanguageFile(appData.language);\r
1022 \r
1023   InitBackEnd1();\r
1024 \r
1025 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1026 //  InitEngineUCI( installDir, &second );\r
1027 \r
1028   /* Create a main window for this application instance. */\r
1029   hwnd = CreateWindow(szAppName, szTitle,\r
1030                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1031                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1032                       NULL, NULL, hInstance, NULL);\r
1033   hwndMain = hwnd;\r
1034 \r
1035   /* If window could not be created, return "failure" */\r
1036   if (!hwnd) {\r
1037     return (FALSE);\r
1038   }\r
1039 \r
1040   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1041   LoadLogo(&first, 0, FALSE);\r
1042   LoadLogo(&second, 1, appData.icsActive);\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, TRUE);\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, TRUE);\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, TRUE);\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, TRUE);\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[] = "0123456789012345678901221098765432109876543210";\r
2696   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\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') : 45+(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 ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\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) {\r
3383     if(!logoHeight) return;\r
3384     FillRect( hdc, &logoRect, whitePieceBrush );\r
3385   }\r
3386 //  GetClientRect(hwndMain, &Rect);\r
3387 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3388 //                                      Rect.bottom-Rect.top+1);\r
3389   tmphdc = CreateCompatibleDC(hdc);\r
3390   hbm = SelectObject(tmphdc, logo);\r
3391   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3392             w = bi.bmWidth;\r
3393             h = bi.bmHeight;\r
3394   }\r
3395   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3396                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3397   SelectObject(tmphdc, hbm);\r
3398   DeleteDC(tmphdc);\r
3399 }\r
3400 \r
3401 VOID\r
3402 DisplayLogos()\r
3403 {\r
3404   if(logoHeight) {\r
3405         HDC hdc = GetDC(hwndMain);\r
3406         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3407         if(appData.autoLogo) {\r
3408           \r
3409           switch(gameMode) { // pick logos based on game mode\r
3410             case IcsObserving:\r
3411                 whiteLogo = second.programLogo; // ICS logo\r
3412                 blackLogo = second.programLogo;\r
3413             default:\r
3414                 break;\r
3415             case IcsPlayingWhite:\r
3416                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3417                 blackLogo = second.programLogo; // ICS logo\r
3418                 break;\r
3419             case IcsPlayingBlack:\r
3420                 whiteLogo = second.programLogo; // ICS logo\r
3421                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3422                 break;\r
3423             case TwoMachinesPlay:\r
3424                 if(first.twoMachinesColor[0] == 'b') {\r
3425                     whiteLogo = second.programLogo;\r
3426                     blackLogo = first.programLogo;\r
3427                 }\r
3428                 break;\r
3429             case MachinePlaysWhite:\r
3430                 blackLogo = userLogo;\r
3431                 break;\r
3432             case MachinePlaysBlack:\r
3433                 whiteLogo = userLogo;\r
3434                 blackLogo = first.programLogo;\r
3435           }\r
3436         }\r
3437         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3438         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3439         ReleaseDC(hwndMain, hdc);\r
3440   }\r
3441 }\r
3442 \r
3443 void\r
3444 UpdateLogos(int display)\r
3445 { // called after loading new engine(s), in tourney or from menu\r
3446   LoadLogo(&first, 0, FALSE);\r
3447   LoadLogo(&second, 1, appData.icsActive);\r
3448   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3449   if(display) DisplayLogos();\r
3450 }\r
3451 \r
3452 static HDC hdcSeek;\r
3453 \r
3454 // [HGM] seekgraph\r
3455 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3456 {\r
3457     POINT stPt;\r
3458     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3459     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3460     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3461     SelectObject( hdcSeek, hp );\r
3462 }\r
3463 \r
3464 // front-end wrapper for drawing functions to do rectangles\r
3465 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3466 {\r
3467     HPEN hp;\r
3468     RECT rc;\r
3469 \r
3470     if (hdcSeek == NULL) {\r
3471     hdcSeek = GetDC(hwndMain);\r
3472       if (!appData.monoMode) {\r
3473         SelectPalette(hdcSeek, hPal, FALSE);\r
3474         RealizePalette(hdcSeek);\r
3475       }\r
3476     }\r
3477     hp = SelectObject( hdcSeek, gridPen );\r
3478     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3479     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3480     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3481     SelectObject( hdcSeek, hp );\r
3482 }\r
3483 \r
3484 // front-end wrapper for putting text in graph\r
3485 void DrawSeekText(char *buf, int x, int y)\r
3486 {\r
3487         SIZE stSize;\r
3488         SetBkMode( hdcSeek, TRANSPARENT );\r
3489         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3490         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3491 }\r
3492 \r
3493 void DrawSeekDot(int x, int y, int color)\r
3494 {\r
3495         int square = color & 0x80;\r
3496         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3497                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3498         color &= 0x7F;\r
3499         if(square)\r
3500             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3501                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3502         else\r
3503             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3504                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3505             SelectObject(hdcSeek, oldBrush);\r
3506 }\r
3507 \r
3508 VOID\r
3509 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3510 {\r
3511   static Board lastReq[2], lastDrawn[2];\r
3512   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3513   static int lastDrawnFlipView = 0;\r
3514   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3515   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3516   HDC tmphdc;\r
3517   HDC hdcmem;\r
3518   HBITMAP bufferBitmap;\r
3519   HBITMAP oldBitmap;\r
3520   RECT Rect;\r
3521   HRGN clips[MAX_CLIPS];\r
3522   ChessSquare dragged_piece = EmptySquare;\r
3523   int nr = twoBoards*partnerUp;\r
3524 \r
3525   /* I'm undecided on this - this function figures out whether a full\r
3526    * repaint is necessary on its own, so there's no real reason to have the\r
3527    * caller tell it that.  I think this can safely be set to FALSE - but\r
3528    * if we trust the callers not to request full repaints unnessesarily, then\r
3529    * we could skip some clipping work.  In other words, only request a full\r
3530    * redraw when the majority of pieces have changed positions (ie. flip, \r
3531    * gamestart and similar)  --Hawk\r
3532    */\r
3533   Boolean fullrepaint = repaint;\r
3534 \r
3535   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3536 \r
3537   if( DrawPositionNeedsFullRepaint() ) {\r
3538       fullrepaint = TRUE;\r
3539   }\r
3540 \r
3541   if (board == NULL) {\r
3542     if (!lastReqValid[nr]) {\r
3543       return;\r
3544     }\r
3545     board = lastReq[nr];\r
3546   } else {\r
3547     CopyBoard(lastReq[nr], board);\r
3548     lastReqValid[nr] = 1;\r
3549   }\r
3550 \r
3551   if (doingSizing) {\r
3552     return;\r
3553   }\r
3554 \r
3555   if (IsIconic(hwndMain)) {\r
3556     return;\r
3557   }\r
3558 \r
3559   if (hdc == NULL) {\r
3560     hdc = GetDC(hwndMain);\r
3561     if (!appData.monoMode) {\r
3562       SelectPalette(hdc, hPal, FALSE);\r
3563       RealizePalette(hdc);\r
3564     }\r
3565     releaseDC = TRUE;\r
3566   } else {\r
3567     releaseDC = FALSE;\r
3568   }\r
3569 \r
3570   /* Create some work-DCs */\r
3571   hdcmem = CreateCompatibleDC(hdc);\r
3572   tmphdc = CreateCompatibleDC(hdc);\r
3573 \r
3574   /* If dragging is in progress, we temporarely remove the piece */\r
3575   /* [HGM] or temporarily decrease count if stacked              */\r
3576   /*       !! Moved to before board compare !!                   */\r
3577   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3578     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3579     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3580             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3581         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3582     } else \r
3583     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3584             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3585         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3586     } else \r
3587         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3588   }\r
3589 \r
3590   /* Figure out which squares need updating by comparing the \r
3591    * newest board with the last drawn board and checking if\r
3592    * flipping has changed.\r
3593    */\r
3594   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3595     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3596       for (column = 0; column < BOARD_WIDTH; column++) {\r
3597         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3598           SquareToPos(row, column, &x, &y);\r
3599           clips[num_clips++] =\r
3600             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3601         }\r
3602       }\r
3603     }\r
3604    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3605     for (i=0; i<2; i++) {\r
3606       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3607           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3608         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3609             lastDrawnHighlight.sq[i].y >= 0) {\r
3610           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3611                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3612           clips[num_clips++] =\r
3613             CreateRectRgn(x - lineGap, y - lineGap, \r
3614                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3615         }\r
3616         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3617           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3618           clips[num_clips++] =\r
3619             CreateRectRgn(x - lineGap, y - lineGap, \r
3620                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3621         }\r
3622       }\r
3623     }\r
3624     for (i=0; i<2; i++) {\r
3625       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3626           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3627         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3628             lastDrawnPremove.sq[i].y >= 0) {\r
3629           SquareToPos(lastDrawnPremove.sq[i].y,\r
3630                       lastDrawnPremove.sq[i].x, &x, &y);\r
3631           clips[num_clips++] =\r
3632             CreateRectRgn(x - lineGap, y - lineGap, \r
3633                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3634         }\r
3635         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3636             premoveHighlightInfo.sq[i].y >= 0) {\r
3637           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3638                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3639           clips[num_clips++] =\r
3640             CreateRectRgn(x - lineGap, y - lineGap, \r
3641                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3642         }\r
3643       }\r
3644     }\r
3645    } else { // nr == 1\r
3646         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3647         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3648         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3649         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3650       for (i=0; i<2; i++) {\r
3651         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3652             partnerHighlightInfo.sq[i].y >= 0) {\r
3653           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3654                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3655           clips[num_clips++] =\r
3656             CreateRectRgn(x - lineGap, y - lineGap, \r
3657                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3658         }\r
3659         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3660             oldPartnerHighlight.sq[i].y >= 0) {\r
3661           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3662                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3663           clips[num_clips++] =\r
3664             CreateRectRgn(x - lineGap, y - lineGap, \r
3665                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3666         }\r
3667       }\r
3668    }\r
3669   } else {\r
3670     fullrepaint = TRUE;\r
3671   }\r
3672 \r
3673   /* Create a buffer bitmap - this is the actual bitmap\r
3674    * being written to.  When all the work is done, we can\r
3675    * copy it to the real DC (the screen).  This avoids\r
3676    * the problems with flickering.\r
3677    */\r
3678   GetClientRect(hwndMain, &Rect);\r
3679   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3680                                         Rect.bottom-Rect.top+1);\r
3681   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3682   if (!appData.monoMode) {\r
3683     SelectPalette(hdcmem, hPal, FALSE);\r
3684   }\r
3685 \r
3686   /* Create clips for dragging */\r
3687   if (!fullrepaint) {\r
3688     if (dragInfo.from.x >= 0) {\r
3689       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3690       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3691     }\r
3692     if (dragInfo.start.x >= 0) {\r
3693       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3694       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3695     }\r
3696     if (dragInfo.pos.x >= 0) {\r
3697       x = dragInfo.pos.x - squareSize / 2;\r
3698       y = dragInfo.pos.y - squareSize / 2;\r
3699       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3700     }\r
3701     if (dragInfo.lastpos.x >= 0) {\r
3702       x = dragInfo.lastpos.x - squareSize / 2;\r
3703       y = dragInfo.lastpos.y - squareSize / 2;\r
3704       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3705     }\r
3706   }\r
3707 \r
3708   /* Are we animating a move?  \r
3709    * If so, \r
3710    *   - remove the piece from the board (temporarely)\r
3711    *   - calculate the clipping region\r
3712    */\r
3713   if (!fullrepaint) {\r
3714     if (animInfo.piece != EmptySquare) {\r
3715       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3716       x = boardRect.left + animInfo.lastpos.x;\r
3717       y = boardRect.top + animInfo.lastpos.y;\r
3718       x2 = boardRect.left + animInfo.pos.x;\r
3719       y2 = boardRect.top + animInfo.pos.y;\r
3720       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3721       /* Slight kludge.  The real problem is that after AnimateMove is\r
3722          done, the position on the screen does not match lastDrawn.\r
3723          This currently causes trouble only on e.p. captures in\r
3724          atomic, where the piece moves to an empty square and then\r
3725          explodes.  The old and new positions both had an empty square\r
3726          at the destination, but animation has drawn a piece there and\r
3727          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3728       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3729     }\r
3730   }\r
3731 \r
3732   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3733   if (num_clips == 0)\r
3734     fullrepaint = TRUE;\r
3735 \r
3736   /* Set clipping on the memory DC */\r
3737   if (!fullrepaint) {\r
3738     SelectClipRgn(hdcmem, clips[0]);\r
3739     for (x = 1; x < num_clips; x++) {\r
3740       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3741         abort();  // this should never ever happen!\r
3742     }\r
3743   }\r
3744 \r
3745   /* Do all the drawing to the memory DC */\r
3746   if(explodeInfo.radius) { // [HGM] atomic\r
3747         HBRUSH oldBrush;\r
3748         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3749         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3750         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3751         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3752         x += squareSize/2;\r
3753         y += squareSize/2;\r
3754         if(!fullrepaint) {\r
3755           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3756           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3757         }\r
3758         DrawGridOnDC(hdcmem);\r
3759         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3760         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3761         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3762         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3763         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3764         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3765         SelectObject(hdcmem, oldBrush);\r
3766   } else {\r
3767     DrawGridOnDC(hdcmem);\r
3768     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3769         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3770         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3771     } else {\r
3772         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3773         oldPartnerHighlight = partnerHighlightInfo;\r
3774     }\r
3775     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3776   }\r
3777   if(nr == 0) // [HGM] dual: markers only on left board\r
3778   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3779     for (column = 0; column < BOARD_WIDTH; column++) {\r
3780         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3781             HBRUSH oldBrush = SelectObject(hdcmem, \r
3782                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3783             SquareToPos(row, column, &x, &y);\r
3784             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3785                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3786             SelectObject(hdcmem, oldBrush);\r
3787         }\r
3788     }\r
3789   }\r
3790 \r
3791   if( appData.highlightMoveWithArrow ) {\r
3792     DrawArrowHighlight(hdcmem);\r
3793   }\r
3794 \r
3795   DrawCoordsOnDC(hdcmem);\r
3796 \r
3797   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3798                  /* to make sure lastDrawn contains what is actually drawn */\r
3799 \r
3800   /* Put the dragged piece back into place and draw it (out of place!) */\r
3801     if (dragged_piece != EmptySquare) {\r
3802     /* [HGM] or restack */\r
3803     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3804                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3805     else\r
3806     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3807                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3808     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3809     x = dragInfo.pos.x - squareSize / 2;\r
3810     y = dragInfo.pos.y - squareSize / 2;\r
3811     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
3812                   ((int) dragInfo.piece < (int) BlackPawn), \r
3813                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3814   }   \r
3815   \r
3816   /* Put the animated piece back into place and draw it */\r
3817   if (animInfo.piece != EmptySquare) {\r
3818     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3819     x = boardRect.left + animInfo.pos.x;\r
3820     y = boardRect.top + animInfo.pos.y;\r
3821     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3822                   ((int) animInfo.piece < (int) BlackPawn),\r
3823                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3824   }\r
3825 \r
3826   /* Release the bufferBitmap by selecting in the old bitmap \r
3827    * and delete the memory DC\r
3828    */\r
3829   SelectObject(hdcmem, oldBitmap);\r
3830   DeleteDC(hdcmem);\r
3831 \r
3832   /* Set clipping on the target DC */\r
3833   if (!fullrepaint) {\r
3834     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3835         RECT rect;\r
3836         GetRgnBox(clips[x], &rect);\r
3837         DeleteObject(clips[x]);\r
3838         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3839                           rect.right + wpMain.width/2, rect.bottom);\r
3840     }\r
3841     SelectClipRgn(hdc, clips[0]);\r
3842     for (x = 1; x < num_clips; x++) {\r
3843       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3844         abort();   // this should never ever happen!\r
3845     } \r
3846   }\r
3847 \r
3848   /* Copy the new bitmap onto the screen in one go.\r
3849    * This way we avoid any flickering\r
3850    */\r
3851   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3852   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3853          boardRect.right - boardRect.left,\r
3854          boardRect.bottom - boardRect.top,\r
3855          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3856   if(saveDiagFlag) { \r
3857     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
3858     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3859 \r
3860     GetObject(bufferBitmap, sizeof(b), &b);\r
3861     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
3862         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3863         bih.biWidth = b.bmWidth;\r
3864         bih.biHeight = b.bmHeight;\r
3865         bih.biPlanes = 1;\r
3866         bih.biBitCount = b.bmBitsPixel;\r
3867         bih.biCompression = 0;\r
3868         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3869         bih.biXPelsPerMeter = 0;\r
3870         bih.biYPelsPerMeter = 0;\r
3871         bih.biClrUsed = 0;\r
3872         bih.biClrImportant = 0;\r
3873 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3874 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3875         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3876 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3877 \r
3878         wb = b.bmWidthBytes;\r
3879         // count colors\r
3880         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3881                 int k = ((int*) pData)[i];\r
3882                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3883                 if(j >= 16) break;\r
3884                 color[j] = k;\r
3885                 if(j >= nrColors) nrColors = j+1;\r
3886         }\r
3887         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3888                 INT p = 0;\r
3889                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3890                     for(w=0; w<(wb>>2); w+=2) {\r
3891                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3892                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3893                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3894                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3895                         pData[p++] = m | j<<4;\r
3896                     }\r
3897                     while(p&3) pData[p++] = 0;\r
3898                 }\r
3899                 fac = 3;\r
3900                 wb = ((wb+31)>>5)<<2;\r
3901         }\r
3902         // write BITMAPFILEHEADER\r
3903         fprintf(diagFile, "BM");\r
3904         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3905         fputDW(diagFile, 0);\r
3906         fputDW(diagFile, 0x36 + (fac?64:0));\r
3907         // write BITMAPINFOHEADER\r
3908         fputDW(diagFile, 40);\r
3909         fputDW(diagFile, b.bmWidth);\r
3910         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3911         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3912         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3913         fputDW(diagFile, 0);\r
3914         fputDW(diagFile, 0);\r
3915         fputDW(diagFile, 0);\r
3916         fputDW(diagFile, 0);\r
3917         fputDW(diagFile, 0);\r
3918         fputDW(diagFile, 0);\r
3919         // write color table\r
3920         if(fac)\r
3921         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3922         // write bitmap data\r
3923         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3924                 fputc(pData[i], diagFile);\r
3925         free(pData);\r
3926      }\r
3927   }\r
3928 \r
3929   SelectObject(tmphdc, oldBitmap);\r
3930 \r
3931   /* Massive cleanup */\r
3932   for (x = 0; x < num_clips; x++)\r
3933     DeleteObject(clips[x]);\r
3934 \r
3935   DeleteDC(tmphdc);\r
3936   DeleteObject(bufferBitmap);\r
3937 \r
3938   if (releaseDC) \r
3939     ReleaseDC(hwndMain, hdc);\r
3940   \r
3941   if (lastDrawnFlipView != flipView && nr == 0) {\r
3942     if (flipView)\r
3943       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3944     else\r
3945       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3946   }\r
3947 \r
3948 /*  CopyBoard(lastDrawn, board);*/\r
3949   lastDrawnHighlight = highlightInfo;\r
3950   lastDrawnPremove   = premoveHighlightInfo;\r
3951   lastDrawnFlipView = flipView;\r
3952   lastDrawnValid[nr] = 1;\r
3953 }\r
3954 \r
3955 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3956 int\r
3957 SaveDiagram(f)\r
3958      FILE *f;\r
3959 {\r
3960     saveDiagFlag = 1; diagFile = f;\r
3961     HDCDrawPosition(NULL, TRUE, NULL);\r
3962     saveDiagFlag = 0;\r
3963 \r
3964     fclose(f);\r
3965     return TRUE;\r
3966 }\r
3967 \r
3968 \r
3969 /*---------------------------------------------------------------------------*\\r
3970 | CLIENT PAINT PROCEDURE\r
3971 |   This is the main event-handler for the WM_PAINT message.\r
3972 |\r
3973 \*---------------------------------------------------------------------------*/\r
3974 VOID\r
3975 PaintProc(HWND hwnd)\r
3976 {\r
3977   HDC         hdc;\r
3978   PAINTSTRUCT ps;\r
3979   HFONT       oldFont;\r
3980 \r
3981   if((hdc = BeginPaint(hwnd, &ps))) {\r
3982     if (IsIconic(hwnd)) {\r
3983       DrawIcon(hdc, 2, 2, iconCurrent);\r
3984     } else {\r
3985       if (!appData.monoMode) {\r
3986         SelectPalette(hdc, hPal, FALSE);\r
3987         RealizePalette(hdc);\r
3988       }\r
3989       HDCDrawPosition(hdc, 1, NULL);\r
3990       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
3991         flipView = !flipView; partnerUp = !partnerUp;\r
3992         HDCDrawPosition(hdc, 1, NULL);\r
3993         flipView = !flipView; partnerUp = !partnerUp;\r
3994       }\r
3995       oldFont =\r
3996         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3997       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
3998                  ETO_CLIPPED|ETO_OPAQUE,\r
3999                  &messageRect, messageText, strlen(messageText), NULL);\r
4000       SelectObject(hdc, oldFont);\r
4001       DisplayBothClocks();\r
4002       DisplayLogos();\r
4003     }\r
4004     EndPaint(hwnd,&ps);\r
4005   }\r
4006 \r
4007   return;\r
4008 }\r
4009 \r
4010 \r
4011 /*\r
4012  * If the user selects on a border boundary, return -1; if off the board,\r
4013  *   return -2.  Otherwise map the event coordinate to the square.\r
4014  * The offset boardRect.left or boardRect.top must already have been\r
4015  *   subtracted from x.\r
4016  */\r
4017 int EventToSquare(x, limit)\r
4018      int x, limit;\r
4019 {\r
4020   if (x <= 0)\r
4021     return -2;\r
4022   if (x < lineGap)\r
4023     return -1;\r
4024   x -= lineGap;\r
4025   if ((x % (squareSize + lineGap)) >= squareSize)\r
4026     return -1;\r
4027   x /= (squareSize + lineGap);\r
4028     if (x >= limit)\r
4029     return -2;\r
4030   return x;\r
4031 }\r
4032 \r
4033 typedef struct {\r
4034   char piece;\r
4035   int command;\r
4036   char* name;\r
4037 } DropEnable;\r
4038 \r
4039 DropEnable dropEnables[] = {\r
4040   { 'P', DP_Pawn, N_("Pawn") },\r
4041   { 'N', DP_Knight, N_("Knight") },\r
4042   { 'B', DP_Bishop, N_("Bishop") },\r
4043   { 'R', DP_Rook, N_("Rook") },\r
4044   { 'Q', DP_Queen, N_("Queen") },\r
4045 };\r
4046 \r
4047 VOID\r
4048 SetupDropMenu(HMENU hmenu)\r
4049 {\r
4050   int i, count, enable;\r
4051   char *p;\r
4052   extern char white_holding[], black_holding[];\r
4053   char item[MSG_SIZ];\r
4054 \r
4055   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4056     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4057                dropEnables[i].piece);\r
4058     count = 0;\r
4059     while (p && *p++ == dropEnables[i].piece) count++;\r
4060       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4061     enable = count > 0 || !appData.testLegality\r
4062       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4063                       && !appData.icsActive);\r
4064     ModifyMenu(hmenu, dropEnables[i].command,\r
4065                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4066                dropEnables[i].command, item);\r
4067   }\r
4068 }\r
4069 \r
4070 void DragPieceBegin(int x, int y, Boolean instantly)\r
4071 {\r
4072       dragInfo.lastpos.x = boardRect.left + x;\r
4073       dragInfo.lastpos.y = boardRect.top + y;\r
4074       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4075       dragInfo.from.x = fromX;\r
4076       dragInfo.from.y = fromY;\r
4077       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4078       dragInfo.start = dragInfo.from;\r
4079       SetCapture(hwndMain);\r
4080 }\r
4081 \r
4082 void DragPieceEnd(int x, int y)\r
4083 {\r
4084     ReleaseCapture();\r
4085     dragInfo.start.x = dragInfo.start.y = -1;\r
4086     dragInfo.from = dragInfo.start;\r
4087     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4088 }\r
4089 \r
4090 void ChangeDragPiece(ChessSquare piece)\r
4091 {\r
4092     dragInfo.piece = piece;\r
4093 }\r
4094 \r
4095 /* Event handler for mouse messages */\r
4096 VOID\r
4097 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4098 {\r
4099   int x, y, menuNr;\r
4100   POINT pt;\r
4101   static int recursive = 0;\r
4102   HMENU hmenu;\r
4103   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4104 \r
4105   if (recursive) {\r
4106     if (message == WM_MBUTTONUP) {\r
4107       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4108          to the middle button: we simulate pressing the left button too!\r
4109          */\r
4110       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4111       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4112     }\r
4113     return;\r
4114   }\r
4115   recursive++;\r
4116   \r
4117   pt.x = LOWORD(lParam);\r
4118   pt.y = HIWORD(lParam);\r
4119   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4120   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4121   if (!flipView && y >= 0) {\r
4122     y = BOARD_HEIGHT - 1 - y;\r
4123   }\r
4124   if (flipView && x >= 0) {\r
4125     x = BOARD_WIDTH - 1 - x;\r
4126   }\r
4127 \r
4128   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4129 \r
4130   switch (message) {\r
4131   case WM_LBUTTONDOWN:\r
4132       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4133         ClockClick(flipClock);\r
4134       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4135         ClockClick(!flipClock);\r
4136       }\r
4137       dragInfo.start.x = dragInfo.start.y = -1;\r
4138       dragInfo.from = dragInfo.start;\r
4139     if(fromX == -1 && frozen) { // not sure where this is for\r
4140                 fromX = fromY = -1; \r
4141       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4142       break;\r
4143     }\r
4144       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4145       DrawPosition(TRUE, NULL);\r
4146     break;\r
4147 \r
4148   case WM_LBUTTONUP:\r
4149       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4150       DrawPosition(TRUE, NULL);\r
4151     break;\r
4152 \r
4153   case WM_MOUSEMOVE:\r
4154     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4155     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4156     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4157     if ((appData.animateDragging || appData.highlightDragging)\r
4158         && (wParam & MK_LBUTTON)\r
4159         && dragInfo.from.x >= 0) \r
4160     {\r
4161       BOOL full_repaint = FALSE;\r
4162 \r
4163       if (appData.animateDragging) {\r
4164         dragInfo.pos = pt;\r
4165       }\r
4166       if (appData.highlightDragging) {\r
4167         SetHighlights(fromX, fromY, x, y);\r
4168         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4169             full_repaint = TRUE;\r
4170         }\r
4171       }\r
4172       \r
4173       DrawPosition( full_repaint, NULL);\r
4174       \r
4175       dragInfo.lastpos = dragInfo.pos;\r
4176     }\r
4177     break;\r
4178 \r
4179   case WM_MOUSEWHEEL: // [DM]\r
4180     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4181        /* Mouse Wheel is being rolled forward\r
4182         * Play moves forward\r
4183         */\r
4184        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4185                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4186        /* Mouse Wheel is being rolled backward\r
4187         * Play moves backward\r
4188         */\r
4189        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4190                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4191     }\r
4192     break;\r
4193 \r
4194   case WM_MBUTTONUP:\r
4195   case WM_RBUTTONUP:\r
4196     ReleaseCapture();\r
4197     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4198     break;\r
4199  \r
4200   case WM_MBUTTONDOWN:\r
4201   case WM_RBUTTONDOWN:\r
4202     ErrorPopDown();\r
4203     ReleaseCapture();\r
4204     fromX = fromY = -1;\r
4205     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4206     dragInfo.start.x = dragInfo.start.y = -1;\r
4207     dragInfo.from = dragInfo.start;\r
4208     dragInfo.lastpos = dragInfo.pos;\r
4209     if (appData.highlightDragging) {\r
4210       ClearHighlights();\r
4211     }\r
4212     if(y == -2) {\r
4213       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4214       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4215           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4216       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4217           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4218       }\r
4219       break;\r
4220     }\r
4221     DrawPosition(TRUE, NULL);\r
4222 \r
4223     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4224     switch (menuNr) {\r
4225     case 0:\r
4226       if (message == WM_MBUTTONDOWN) {\r
4227         buttonCount = 3;  /* even if system didn't think so */\r
4228         if (wParam & MK_SHIFT) \r
4229           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4230         else\r
4231           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4232       } else { /* message == WM_RBUTTONDOWN */\r
4233         /* Just have one menu, on the right button.  Windows users don't\r
4234            think to try the middle one, and sometimes other software steals\r
4235            it, or it doesn't really exist. */\r
4236         if(gameInfo.variant != VariantShogi)\r
4237             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4238         else\r
4239             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4240       }\r
4241       break;\r
4242     case 2:\r
4243       SetCapture(hwndMain);
4244       break;\r
4245     case 1:\r
4246       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4247       SetupDropMenu(hmenu);\r
4248       MenuPopup(hwnd, pt, hmenu, -1);\r
4249     default:\r
4250       break;\r
4251     }\r
4252     break;\r
4253   }\r
4254 \r
4255   recursive--;\r
4256 }\r
4257 \r
4258 /* Preprocess messages for buttons in main window */\r
4259 LRESULT CALLBACK\r
4260 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4261 {\r
4262   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4263   int i, dir;\r
4264 \r
4265   for (i=0; i<N_BUTTONS; i++) {\r
4266     if (buttonDesc[i].id == id) break;\r
4267   }\r
4268   if (i == N_BUTTONS) return 0;\r
4269   switch (message) {\r
4270   case WM_KEYDOWN:\r
4271     switch (wParam) {\r
4272     case VK_LEFT:\r
4273     case VK_RIGHT:\r
4274       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4275       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4276       return TRUE;\r
4277     }\r
4278     break;\r
4279   case WM_CHAR:\r
4280     switch (wParam) {\r
4281     case '\r':\r
4282       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4283       return TRUE;\r
4284     default:\r
4285       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4286         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4287         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4288         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4289         SetFocus(h);\r
4290         SendMessage(h, WM_CHAR, wParam, lParam);\r
4291         return TRUE;\r
4292       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4293         TypeInEvent((char)wParam);\r
4294       }\r
4295       break;\r
4296     }\r
4297     break;\r
4298   }\r
4299   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4300 }\r
4301 \r
4302 /* Process messages for Promotion dialog box */\r
4303 LRESULT CALLBACK\r
4304 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4305 {\r
4306   char promoChar;\r
4307 \r
4308   switch (message) {\r
4309   case WM_INITDIALOG: /* message: initialize dialog box */\r
4310     /* Center the dialog over the application window */\r
4311     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4312     Translate(hDlg, DLG_PromotionKing);\r
4313     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4314       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4315        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4316        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4317                SW_SHOW : SW_HIDE);\r
4318     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4319     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4320        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4321          PieceToChar(WhiteAngel) != '~') ||\r
4322         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4323          PieceToChar(BlackAngel) != '~')   ) ?\r
4324                SW_SHOW : SW_HIDE);\r
4325     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4326        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4327          PieceToChar(WhiteMarshall) != '~') ||\r
4328         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4329          PieceToChar(BlackMarshall) != '~')   ) ?\r
4330                SW_SHOW : SW_HIDE);\r
4331     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4332     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4333        gameInfo.variant != VariantShogi ?\r
4334                SW_SHOW : SW_HIDE);\r
4335     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4336        gameInfo.variant != VariantShogi ?\r
4337                SW_SHOW : SW_HIDE);\r
4338     if(gameInfo.variant == VariantShogi) {\r
4339         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4340         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4341         SetWindowText(hDlg, "Promote?");\r
4342     }\r
4343     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4344        gameInfo.variant == VariantSuper ?\r
4345                SW_SHOW : SW_HIDE);\r
4346     return TRUE;\r
4347 \r
4348   case WM_COMMAND: /* message: received a command */\r
4349     switch (LOWORD(wParam)) {\r
4350     case IDCANCEL:\r
4351       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4352       ClearHighlights();\r
4353       DrawPosition(FALSE, NULL);\r
4354       return TRUE;\r
4355     case PB_King:\r
4356       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4357       break;\r
4358     case PB_Queen:\r
4359       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4360       break;\r
4361     case PB_Rook:\r
4362       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4363       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4364       break;\r
4365     case PB_Bishop:\r
4366       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4367       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4368       break;\r
4369     case PB_Chancellor:\r
4370       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4371       break;\r
4372     case PB_Archbishop:\r
4373       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4374       break;\r
4375     case PB_Knight:\r
4376       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4377       break;\r
4378     default:\r
4379       return FALSE;\r
4380     }\r
4381     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4382     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4383     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4384     fromX = fromY = -1;\r
4385     if (!appData.highlightLastMove) {\r
4386       ClearHighlights();\r
4387       DrawPosition(FALSE, NULL);\r
4388     }\r
4389     return TRUE;\r
4390   }\r
4391   return FALSE;\r
4392 }\r
4393 \r
4394 /* Pop up promotion dialog */\r
4395 VOID\r
4396 PromotionPopup(HWND hwnd)\r
4397 {\r
4398   FARPROC lpProc;\r
4399 \r
4400   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4401   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4402     hwnd, (DLGPROC)lpProc);\r
4403   FreeProcInstance(lpProc);\r
4404 }\r
4405 \r
4406 void\r
4407 PromotionPopUp()\r
4408 {\r
4409   DrawPosition(TRUE, NULL);\r
4410   PromotionPopup(hwndMain);\r
4411 }\r
4412 \r
4413 /* Toggle ShowThinking */\r
4414 VOID\r
4415 ToggleShowThinking()\r
4416 {\r
4417   appData.showThinking = !appData.showThinking;\r
4418   ShowThinkingEvent();\r
4419 }\r
4420 \r
4421 VOID\r
4422 LoadGameDialog(HWND hwnd, char* title)\r
4423 {\r
4424   UINT number = 0;\r
4425   FILE *f;\r
4426   char fileTitle[MSG_SIZ];\r
4427   f = OpenFileDialog(hwnd, "rb", "",\r
4428                      appData.oldSaveStyle ? "gam" : "pgn",\r
4429                      GAME_FILT,\r
4430                      title, &number, fileTitle, NULL);\r
4431   if (f != NULL) {\r
4432     cmailMsgLoaded = FALSE;\r
4433     if (number == 0) {\r
4434       int error = GameListBuild(f);\r
4435       if (error) {\r
4436         DisplayError(_("Cannot build game list"), error);\r
4437       } else if (!ListEmpty(&gameList) &&\r
4438                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4439         GameListPopUp(f, fileTitle);\r
4440         return;\r
4441       }\r
4442       GameListDestroy();\r
4443       number = 1;\r
4444     }\r
4445     LoadGame(f, number, fileTitle, FALSE);\r
4446   }\r
4447 }\r
4448 \r
4449 int get_term_width()\r
4450 {\r
4451     HDC hdc;\r
4452     TEXTMETRIC tm;\r
4453     RECT rc;\r
4454     HFONT hfont, hold_font;\r
4455     LOGFONT lf;\r
4456     HWND hText;\r
4457 \r
4458     if (hwndConsole)\r
4459         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4460     else\r
4461         return 79;\r
4462 \r
4463     // get the text metrics\r
4464     hdc = GetDC(hText);\r
4465     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4466     if (consoleCF.dwEffects & CFE_BOLD)\r
4467         lf.lfWeight = FW_BOLD;\r
4468     if (consoleCF.dwEffects & CFE_ITALIC)\r
4469         lf.lfItalic = TRUE;\r
4470     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4471         lf.lfStrikeOut = TRUE;\r
4472     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4473         lf.lfUnderline = TRUE;\r
4474     hfont = CreateFontIndirect(&lf);\r
4475     hold_font = SelectObject(hdc, hfont);\r
4476     GetTextMetrics(hdc, &tm);\r
4477     SelectObject(hdc, hold_font);\r
4478     DeleteObject(hfont);\r
4479     ReleaseDC(hText, hdc);\r
4480 \r
4481     // get the rectangle\r
4482     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4483 \r
4484     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4485 }\r
4486 \r
4487 void UpdateICSWidth(HWND hText)\r
4488 {\r
4489     LONG old_width, new_width;\r
4490 \r
4491     new_width = get_term_width(hText, FALSE);\r
4492     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4493     if (new_width != old_width)\r
4494     {\r
4495         ics_update_width(new_width);\r
4496         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4497     }\r
4498 }\r
4499 \r
4500 VOID\r
4501 ChangedConsoleFont()\r
4502 {\r
4503   CHARFORMAT cfmt;\r
4504   CHARRANGE tmpsel, sel;\r
4505   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4506   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4507   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4508   PARAFORMAT paraf;\r
4509 \r
4510   cfmt.cbSize = sizeof(CHARFORMAT);\r
4511   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4512     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4513                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4514   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4515    * size.  This was undocumented in the version of MSVC++ that I had\r
4516    * when I wrote the code, but is apparently documented now.\r
4517    */\r
4518   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4519   cfmt.bCharSet = f->lf.lfCharSet;\r
4520   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4521   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4522   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4523   /* Why are the following seemingly needed too? */\r
4524   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4525   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4526   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4527   tmpsel.cpMin = 0;\r
4528   tmpsel.cpMax = -1; /*999999?*/\r
4529   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4530   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4531   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4532    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4533    */\r
4534   paraf.cbSize = sizeof(paraf);\r
4535   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4536   paraf.dxStartIndent = 0;\r
4537   paraf.dxOffset = WRAP_INDENT;\r
4538   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4539   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4540   UpdateICSWidth(hText);\r
4541 }\r
4542 \r
4543 /*---------------------------------------------------------------------------*\\r
4544  *\r
4545  * Window Proc for main window\r
4546  *\r
4547 \*---------------------------------------------------------------------------*/\r
4548 \r
4549 /* Process messages for main window, etc. */\r
4550 LRESULT CALLBACK\r
4551 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4552 {\r
4553   FARPROC lpProc;\r
4554   int wmId, wmEvent;\r
4555   char *defName;\r
4556   FILE *f;\r
4557   UINT number;\r
4558   char fileTitle[MSG_SIZ];\r
4559   char buf[MSG_SIZ];\r
4560   static SnapData sd;\r
4561 \r
4562   switch (message) {\r
4563 \r
4564   case WM_PAINT: /* message: repaint portion of window */\r
4565     PaintProc(hwnd);\r
4566     break;\r
4567 \r
4568   case WM_ERASEBKGND:\r
4569     if (IsIconic(hwnd)) {\r
4570       /* Cheat; change the message */\r
4571       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4572     } else {\r
4573       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4574     }\r
4575     break;\r
4576 \r
4577   case WM_LBUTTONDOWN:\r
4578   case WM_MBUTTONDOWN:\r
4579   case WM_RBUTTONDOWN:\r
4580   case WM_LBUTTONUP:\r
4581   case WM_MBUTTONUP:\r
4582   case WM_RBUTTONUP:\r
4583   case WM_MOUSEMOVE:\r
4584   case WM_MOUSEWHEEL:\r
4585     MouseEvent(hwnd, message, wParam, lParam);\r
4586     break;\r
4587 \r
4588   JAWS_KB_NAVIGATION\r
4589 \r
4590   case WM_CHAR:\r
4591     \r
4592     JAWS_ALT_INTERCEPT\r
4593 \r
4594     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4595         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4596         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4597         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4598         SetFocus(h);\r
4599         SendMessage(h, message, wParam, lParam);\r
4600     } else if(lParam != KF_REPEAT) {\r
4601         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4602                 TypeInEvent((char)wParam);\r
4603         } else if((char)wParam == 003) CopyGameToClipboard();\r
4604          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4605     }\r
4606 \r
4607     break;\r
4608 \r
4609   case WM_PALETTECHANGED:\r
4610     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4611       int nnew;\r
4612       HDC hdc = GetDC(hwndMain);\r
4613       SelectPalette(hdc, hPal, TRUE);\r
4614       nnew = RealizePalette(hdc);\r
4615       if (nnew > 0) {\r
4616         paletteChanged = TRUE;\r
4617         InvalidateRect(hwnd, &boardRect, FALSE);\r
4618       }\r
4619       ReleaseDC(hwnd, hdc);\r
4620     }\r
4621     break;\r
4622 \r
4623   case WM_QUERYNEWPALETTE:\r
4624     if (!appData.monoMode /*&& paletteChanged*/) {\r
4625       int nnew;\r
4626       HDC hdc = GetDC(hwndMain);\r
4627       paletteChanged = FALSE;\r
4628       SelectPalette(hdc, hPal, FALSE);\r
4629       nnew = RealizePalette(hdc);\r
4630       if (nnew > 0) {\r
4631         InvalidateRect(hwnd, &boardRect, FALSE);\r
4632       }\r
4633       ReleaseDC(hwnd, hdc);\r
4634       return TRUE;\r
4635     }\r
4636     return FALSE;\r
4637 \r
4638   case WM_COMMAND: /* message: command from application menu */\r
4639     wmId    = LOWORD(wParam);\r
4640     wmEvent = HIWORD(wParam);\r
4641 \r
4642     switch (wmId) {\r
4643     case IDM_NewGame:\r
4644       ResetGameEvent();\r
4645       SAY("new game enter a move to play against the computer with white");\r
4646       break;\r
4647 \r
4648     case IDM_NewGameFRC:\r
4649       if( NewGameFRC() == 0 ) {\r
4650         ResetGameEvent();\r
4651       }\r
4652       break;\r
4653 \r
4654     case IDM_NewVariant:\r
4655       NewVariantPopup(hwnd);\r
4656       break;\r
4657 \r
4658     case IDM_LoadGame:\r
4659       LoadGameDialog(hwnd, _("Load Game from File"));\r
4660       break;\r
4661 \r
4662     case IDM_LoadNextGame:\r
4663       ReloadGame(1);\r
4664       break;\r
4665 \r
4666     case IDM_LoadPrevGame:\r
4667       ReloadGame(-1);\r
4668       break;\r
4669 \r
4670     case IDM_ReloadGame:\r
4671       ReloadGame(0);\r
4672       break;\r
4673 \r
4674     case IDM_LoadPosition:\r
4675       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4676         Reset(FALSE, TRUE);\r
4677       }\r
4678       number = 1;\r
4679       f = OpenFileDialog(hwnd, "rb", "",\r
4680                          appData.oldSaveStyle ? "pos" : "fen",\r
4681                          POSITION_FILT,\r
4682                          _("Load Position from File"), &number, fileTitle, NULL);\r
4683       if (f != NULL) {\r
4684         LoadPosition(f, number, fileTitle);\r
4685       }\r
4686       break;\r
4687 \r
4688     case IDM_LoadNextPosition:\r
4689       ReloadPosition(1);\r
4690       break;\r
4691 \r
4692     case IDM_LoadPrevPosition:\r
4693       ReloadPosition(-1);\r
4694       break;\r
4695 \r
4696     case IDM_ReloadPosition:\r
4697       ReloadPosition(0);\r
4698       break;\r
4699 \r
4700     case IDM_SaveGame:\r
4701       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4702       f = OpenFileDialog(hwnd, "a", defName,\r
4703                          appData.oldSaveStyle ? "gam" : "pgn",\r
4704                          GAME_FILT,\r
4705                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4706       if (f != NULL) {\r
4707         SaveGame(f, 0, "");\r
4708       }\r
4709       break;\r
4710 \r
4711     case IDM_SavePosition:\r
4712       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4713       f = OpenFileDialog(hwnd, "a", defName,\r
4714                          appData.oldSaveStyle ? "pos" : "fen",\r
4715                          POSITION_FILT,\r
4716                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4717       if (f != NULL) {\r
4718         SavePosition(f, 0, "");\r
4719       }\r
4720       break;\r
4721 \r
4722     case IDM_SaveDiagram:\r
4723       defName = "diagram";\r
4724       f = OpenFileDialog(hwnd, "wb", defName,\r
4725                          "bmp",\r
4726                          DIAGRAM_FILT,\r
4727                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4728       if (f != NULL) {\r
4729         SaveDiagram(f);\r
4730       }\r
4731       break;\r
4732 \r
4733     case IDM_CopyGame:\r
4734       CopyGameToClipboard();\r
4735       break;\r
4736 \r
4737     case IDM_PasteGame:\r
4738       PasteGameFromClipboard();\r
4739       break;\r
4740 \r
4741     case IDM_CopyGameListToClipboard:\r
4742       CopyGameListToClipboard();\r
4743       break;\r
4744 \r
4745     /* [AS] Autodetect FEN or PGN data */\r
4746     case IDM_PasteAny:\r
4747       PasteGameOrFENFromClipboard();\r
4748       break;\r
4749 \r
4750     /* [AS] Move history */\r
4751     case IDM_ShowMoveHistory:\r
4752         if( MoveHistoryIsUp() ) {\r
4753             MoveHistoryPopDown();\r
4754         }\r
4755         else {\r
4756             MoveHistoryPopUp();\r
4757         }\r
4758         break;\r
4759 \r
4760     /* [AS] Eval graph */\r
4761     case IDM_ShowEvalGraph:\r
4762         if( EvalGraphIsUp() ) {\r
4763             EvalGraphPopDown();\r
4764         }\r
4765         else {\r
4766             EvalGraphPopUp();\r
4767             SetFocus(hwndMain);\r
4768         }\r
4769         break;\r
4770 \r
4771     /* [AS] Engine output */\r
4772     case IDM_ShowEngineOutput:\r
4773         if( EngineOutputIsUp() ) {\r
4774             EngineOutputPopDown();\r
4775         }\r
4776         else {\r
4777             EngineOutputPopUp();\r
4778         }\r
4779         break;\r
4780 \r
4781     /* [AS] User adjudication */\r
4782     case IDM_UserAdjudication_White:\r
4783         UserAdjudicationEvent( +1 );\r
4784         break;\r
4785 \r
4786     case IDM_UserAdjudication_Black:\r
4787         UserAdjudicationEvent( -1 );\r
4788         break;\r
4789 \r
4790     case IDM_UserAdjudication_Draw:\r
4791         UserAdjudicationEvent( 0 );\r
4792         break;\r
4793 \r
4794     /* [AS] Game list options dialog */\r
4795     case IDM_GameListOptions:\r
4796       GameListOptions();\r
4797       break;\r
4798 \r
4799     case IDM_NewChat:\r
4800       ChatPopUp(NULL);\r
4801       break;\r
4802 \r
4803     case IDM_CopyPosition:\r
4804       CopyFENToClipboard();\r
4805       break;\r
4806 \r
4807     case IDM_PastePosition:\r
4808       PasteFENFromClipboard();\r
4809       break;\r
4810 \r
4811     case IDM_MailMove:\r
4812       MailMoveEvent();\r
4813       break;\r
4814 \r
4815     case IDM_ReloadCMailMsg:\r
4816       Reset(TRUE, TRUE);\r
4817       ReloadCmailMsgEvent(FALSE);\r
4818       break;\r
4819 \r
4820     case IDM_Minimize:\r
4821       ShowWindow(hwnd, SW_MINIMIZE);\r
4822       break;\r
4823 \r
4824     case IDM_Exit:\r
4825       ExitEvent(0);\r
4826       break;\r
4827 \r
4828     case IDM_MachineWhite:\r
4829       MachineWhiteEvent();\r
4830       /*\r
4831        * refresh the tags dialog only if it's visible\r
4832        */\r
4833       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4834           char *tags;\r
4835           tags = PGNTags(&gameInfo);\r
4836           TagsPopUp(tags, CmailMsg());\r
4837           free(tags);\r
4838       }\r
4839       SAY("computer starts playing white");\r
4840       break;\r
4841 \r
4842     case IDM_MachineBlack:\r
4843       MachineBlackEvent();\r
4844       /*\r
4845        * refresh the tags dialog only if it's visible\r
4846        */\r
4847       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4848           char *tags;\r
4849           tags = PGNTags(&gameInfo);\r
4850           TagsPopUp(tags, CmailMsg());\r
4851           free(tags);\r
4852       }\r
4853       SAY("computer starts playing black");\r
4854       break;\r
4855 \r
4856     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4857       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
4858       break;\r
4859 \r
4860     case IDM_TwoMachines:\r
4861       TwoMachinesEvent();\r
4862       /*\r
4863        * refresh the tags dialog only if it's visible\r
4864        */\r
4865       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4866           char *tags;\r
4867           tags = PGNTags(&gameInfo);\r
4868           TagsPopUp(tags, CmailMsg());\r
4869           free(tags);\r
4870       }\r
4871       SAY("computer starts playing both sides");\r
4872       break;\r
4873 \r
4874     case IDM_AnalysisMode:\r
4875       if (!first.analysisSupport) {\r
4876         snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4877         DisplayError(buf, 0);\r
4878       } else {\r
4879         SAY("analyzing current position");\r
4880         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4881         if (appData.icsActive) {\r
4882                if (gameMode != IcsObserving) {\r
4883                  snprintf(buf, MSG_SIZ, "You are not observing a game");\r
4884                        DisplayError(buf, 0);\r
4885                        /* secure check */\r
4886                        if (appData.icsEngineAnalyze) {\r
4887                                if (appData.debugMode) \r
4888                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4889                                ExitAnalyzeMode();\r
4890                                ModeHighlight();\r
4891                                break;\r
4892                        }\r
4893                        break;\r
4894                } else {\r
4895                        /* if enable, user want disable icsEngineAnalyze */\r
4896                        if (appData.icsEngineAnalyze) {\r
4897                                ExitAnalyzeMode();\r
4898                                ModeHighlight();\r
4899                                break;\r
4900                        }\r
4901                        appData.icsEngineAnalyze = TRUE;\r
4902                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4903                }\r
4904         } \r
4905         if (!appData.showThinking) ToggleShowThinking();\r
4906         AnalyzeModeEvent();\r
4907       }\r
4908       break;\r
4909 \r
4910     case IDM_AnalyzeFile:\r
4911       if (!first.analysisSupport) {\r
4912         char buf[MSG_SIZ];\r
4913           snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4914         DisplayError(buf, 0);\r
4915       } else {\r
4916         if (!appData.showThinking) ToggleShowThinking();\r
4917         AnalyzeFileEvent();\r
4918         LoadGameDialog(hwnd, _("Analyze Game from File"));\r
4919         AnalysisPeriodicEvent(1);\r
4920       }\r
4921       break;\r
4922 \r
4923     case IDM_IcsClient:\r
4924       IcsClientEvent();\r
4925       break;\r
4926 \r
4927     case IDM_EditGame:\r
4928     case IDM_EditGame2:\r
4929       EditGameEvent();\r
4930       SAY("edit game");\r
4931       break;\r
4932 \r
4933     case IDM_EditPosition:\r
4934     case IDM_EditPosition2:\r
4935       EditPositionEvent();\r
4936       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4937       break;\r
4938 \r
4939     case IDM_Training:\r
4940       TrainingEvent();\r
4941       break;\r
4942 \r
4943     case IDM_ShowGameList:\r
4944       ShowGameListProc();\r
4945       break;\r
4946 \r
4947     case IDM_EditProgs1:\r
4948       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
4949       break;\r
4950 \r
4951     case IDM_EditProgs2:\r
4952      LoadEnginePopUp(hwndMain);\r
4953 //      EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);\r
4954       break;\r
4955 \r
4956     case IDM_EditServers:\r
4957       EditTagsPopUp(icsNames, &icsNames);\r
4958       break;\r
4959 \r
4960     case IDM_EditTags:\r
4961     case IDM_Tags:\r
4962       EditTagsProc();\r
4963       break;\r
4964 \r
4965     case IDM_EditBook:\r
4966       EditBookEvent();\r
4967       break;\r
4968 \r
4969     case IDM_EditComment:\r
4970     case IDM_Comment:\r
4971       if (commentUp && editComment) {\r
4972         CommentPopDown();\r
4973       } else {\r
4974         EditCommentEvent();\r
4975       }\r
4976       break;\r
4977 \r
4978     case IDM_Pause:\r
4979       PauseEvent();\r
4980       break;\r
4981 \r
4982     case IDM_Accept:\r
4983       AcceptEvent();\r
4984       break;\r
4985 \r
4986     case IDM_Decline:\r
4987       DeclineEvent();\r
4988       break;\r
4989 \r
4990     case IDM_Rematch:\r
4991       RematchEvent();\r
4992       break;\r
4993 \r
4994     case IDM_CallFlag:\r
4995       CallFlagEvent();\r
4996       break;\r
4997 \r
4998     case IDM_Draw:\r
4999       DrawEvent();\r
5000       break;\r
5001 \r
5002     case IDM_Adjourn:\r
5003       AdjournEvent();\r
5004       break;\r
5005 \r
5006     case IDM_Abort:\r
5007       AbortEvent();\r
5008       break;\r
5009 \r
5010     case IDM_Resign:\r
5011       ResignEvent();\r
5012       break;\r
5013 \r
5014     case IDM_StopObserving:\r
5015       StopObservingEvent();\r
5016       break;\r
5017 \r
5018     case IDM_StopExamining:\r
5019       StopExaminingEvent();\r
5020       break;\r
5021 \r
5022     case IDM_Upload:\r
5023       UploadGameEvent();\r
5024       break;\r
5025 \r
5026     case IDM_TypeInMove:\r
5027       TypeInEvent('\000');\r
5028       break;\r
5029 \r
5030     case IDM_TypeInName:\r
5031       PopUpNameDialog('\000');\r
5032       break;\r
5033 \r
5034     case IDM_Backward:\r
5035       BackwardEvent();\r
5036       SetFocus(hwndMain);\r
5037       break;\r
5038 \r
5039     JAWS_MENU_ITEMS\r
5040 \r
5041     case IDM_Forward:\r
5042       ForwardEvent();\r
5043       SetFocus(hwndMain);\r
5044       break;\r
5045 \r
5046     case IDM_ToStart:\r
5047       ToStartEvent();\r
5048       SetFocus(hwndMain);\r
5049       break;\r
5050 \r
5051     case IDM_ToEnd:\r
5052       ToEndEvent();\r
5053       SetFocus(hwndMain);\r
5054       break;\r
5055 \r
5056     case IDM_Revert:\r
5057       RevertEvent(FALSE);\r
5058       break;\r
5059 \r
5060     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5061       RevertEvent(TRUE);\r
5062       break;\r
5063 \r
5064     case IDM_TruncateGame:\r
5065       TruncateGameEvent();\r
5066       break;\r
5067 \r
5068     case IDM_MoveNow:\r
5069       MoveNowEvent();\r
5070       break;\r
5071 \r
5072     case IDM_RetractMove:\r
5073       RetractMoveEvent();\r
5074       break;\r
5075 \r
5076     case IDM_FlipView:\r
5077       flipView = !flipView;\r
5078       DrawPosition(FALSE, NULL);\r
5079       break;\r
5080 \r
5081     case IDM_FlipClock:\r
5082       flipClock = !flipClock;\r
5083       DisplayBothClocks();\r
5084       DisplayLogos();\r
5085       break;\r
5086 \r
5087     case IDM_MuteSounds:\r
5088       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5089       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5090                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5091       break;\r
5092 \r
5093     case IDM_GeneralOptions:\r
5094       GeneralOptionsPopup(hwnd);\r
5095       DrawPosition(TRUE, NULL);\r
5096       break;\r
5097 \r
5098     case IDM_BoardOptions:\r
5099       BoardOptionsPopup(hwnd);\r
5100       break;\r
5101 \r
5102     case IDM_EnginePlayOptions:\r
5103       EnginePlayOptionsPopup(hwnd);\r
5104       break;\r
5105 \r
5106     case IDM_Engine1Options:\r
5107       EngineOptionsPopup(hwnd, &first);\r
5108       break;\r
5109 \r
5110     case IDM_Engine2Options:\r
5111       savedHwnd = hwnd;\r
5112       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5113       EngineOptionsPopup(hwnd, &second);\r
5114       break;\r
5115 \r
5116     case IDM_OptionsUCI:\r
5117       UciOptionsPopup(hwnd);\r
5118       break;\r
5119 \r
5120     case IDM_Tourney:\r
5121       TourneyPopup(hwnd);\r
5122       break;\r
5123 \r
5124     case IDM_IcsOptions:\r
5125       IcsOptionsPopup(hwnd);\r
5126       break;\r
5127 \r
5128     case IDM_Fonts:\r
5129       FontsOptionsPopup(hwnd);\r
5130       break;\r
5131 \r
5132     case IDM_Sounds:\r
5133       SoundOptionsPopup(hwnd);\r
5134       break;\r
5135 \r
5136     case IDM_CommPort:\r
5137       CommPortOptionsPopup(hwnd);\r
5138       break;\r
5139 \r
5140     case IDM_LoadOptions:\r
5141       LoadOptionsPopup(hwnd);\r
5142       break;\r
5143 \r
5144     case IDM_SaveOptions:\r
5145       SaveOptionsPopup(hwnd);\r
5146       break;\r
5147 \r
5148     case IDM_TimeControl:\r
5149       TimeControlOptionsPopup(hwnd);\r
5150       break;\r
5151 \r
5152     case IDM_SaveSettings:\r
5153       SaveSettings(settingsFileName);\r
5154       break;\r
5155 \r
5156     case IDM_SaveSettingsOnExit:\r
5157       saveSettingsOnExit = !saveSettingsOnExit;\r
5158       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5159                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5160                                          MF_CHECKED : MF_UNCHECKED));\r
5161       break;\r
5162 \r
5163     case IDM_Hint:\r
5164       HintEvent();\r
5165       break;\r
5166 \r
5167     case IDM_Book:\r
5168       BookEvent();\r
5169       break;\r
5170 \r
5171     case IDM_AboutGame:\r
5172       AboutGameEvent();\r
5173       break;\r
5174 \r
5175     case IDM_Debug:\r
5176       appData.debugMode = !appData.debugMode;\r
5177       if (appData.debugMode) {\r
5178         char dir[MSG_SIZ];\r
5179         GetCurrentDirectory(MSG_SIZ, dir);\r
5180         SetCurrentDirectory(installDir);\r
5181         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5182         SetCurrentDirectory(dir);\r
5183         setbuf(debugFP, NULL);\r
5184       } else {\r
5185         fclose(debugFP);\r
5186         debugFP = NULL;\r
5187       }\r
5188       break;\r
5189 \r
5190     case IDM_HELPCONTENTS:\r
5191       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5192           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5193           MessageBox (GetFocus(),\r
5194                     _("Unable to activate help"),\r
5195                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5196       }\r
5197       break;\r
5198 \r
5199     case IDM_HELPSEARCH:\r
5200         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5201             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5202         MessageBox (GetFocus(),\r
5203                     _("Unable to activate help"),\r
5204                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5205       }\r
5206       break;\r
5207 \r
5208     case IDM_HELPHELP:\r
5209       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5210         MessageBox (GetFocus(),\r
5211                     _("Unable to activate help"),\r
5212                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5213       }\r
5214       break;\r
5215 \r
5216     case IDM_ABOUT:\r
5217       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5218       DialogBox(hInst, \r
5219         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5220         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5221       FreeProcInstance(lpProc);\r
5222       break;\r
5223 \r
5224     case IDM_DirectCommand1:\r
5225       AskQuestionEvent(_("Direct Command"),\r
5226                        _("Send to chess program:"), "", "1");\r
5227       break;\r
5228     case IDM_DirectCommand2:\r
5229       AskQuestionEvent(_("Direct Command"),\r
5230                        _("Send to second chess program:"), "", "2");\r
5231       break;\r
5232 \r
5233     case EP_WhitePawn:\r
5234       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5235       fromX = fromY = -1;\r
5236       break;\r
5237 \r
5238     case EP_WhiteKnight:\r
5239       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5240       fromX = fromY = -1;\r
5241       break;\r
5242 \r
5243     case EP_WhiteBishop:\r
5244       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5245       fromX = fromY = -1;\r
5246       break;\r
5247 \r
5248     case EP_WhiteRook:\r
5249       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5250       fromX = fromY = -1;\r
5251       break;\r
5252 \r
5253     case EP_WhiteQueen:\r
5254       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5255       fromX = fromY = -1;\r
5256       break;\r
5257 \r
5258     case EP_WhiteFerz:\r
5259       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5260       fromX = fromY = -1;\r
5261       break;\r
5262 \r
5263     case EP_WhiteWazir:\r
5264       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5265       fromX = fromY = -1;\r
5266       break;\r
5267 \r
5268     case EP_WhiteAlfil:\r
5269       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5270       fromX = fromY = -1;\r
5271       break;\r
5272 \r
5273     case EP_WhiteCannon:\r
5274       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5275       fromX = fromY = -1;\r
5276       break;\r
5277 \r
5278     case EP_WhiteCardinal:\r
5279       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5280       fromX = fromY = -1;\r
5281       break;\r
5282 \r
5283     case EP_WhiteMarshall:\r
5284       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5285       fromX = fromY = -1;\r
5286       break;\r
5287 \r
5288     case EP_WhiteKing:\r
5289       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5290       fromX = fromY = -1;\r
5291       break;\r
5292 \r
5293     case EP_BlackPawn:\r
5294       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5295       fromX = fromY = -1;\r
5296       break;\r
5297 \r
5298     case EP_BlackKnight:\r
5299       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5300       fromX = fromY = -1;\r
5301       break;\r
5302 \r
5303     case EP_BlackBishop:\r
5304       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5305       fromX = fromY = -1;\r
5306       break;\r
5307 \r
5308     case EP_BlackRook:\r
5309       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5310       fromX = fromY = -1;\r
5311       break;\r
5312 \r
5313     case EP_BlackQueen:\r
5314       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5315       fromX = fromY = -1;\r
5316       break;\r
5317 \r
5318     case EP_BlackFerz:\r
5319       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5320       fromX = fromY = -1;\r
5321       break;\r
5322 \r
5323     case EP_BlackWazir:\r
5324       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5325       fromX = fromY = -1;\r
5326       break;\r
5327 \r
5328     case EP_BlackAlfil:\r
5329       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5330       fromX = fromY = -1;\r
5331       break;\r
5332 \r
5333     case EP_BlackCannon:\r
5334       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5335       fromX = fromY = -1;\r
5336       break;\r
5337 \r
5338     case EP_BlackCardinal:\r
5339       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5340       fromX = fromY = -1;\r
5341       break;\r
5342 \r
5343     case EP_BlackMarshall:\r
5344       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5345       fromX = fromY = -1;\r
5346       break;\r
5347 \r
5348     case EP_BlackKing:\r
5349       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5350       fromX = fromY = -1;\r
5351       break;\r
5352 \r
5353     case EP_EmptySquare:\r
5354       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5355       fromX = fromY = -1;\r
5356       break;\r
5357 \r
5358     case EP_ClearBoard:\r
5359       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5360       fromX = fromY = -1;\r
5361       break;\r
5362 \r
5363     case EP_White:\r
5364       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5365       fromX = fromY = -1;\r
5366       break;\r
5367 \r
5368     case EP_Black:\r
5369       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5370       fromX = fromY = -1;\r
5371       break;\r
5372 \r
5373     case EP_Promote:\r
5374       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5375       fromX = fromY = -1;\r
5376       break;\r
5377 \r
5378     case EP_Demote:\r
5379       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5380       fromX = fromY = -1;\r
5381       break;\r
5382 \r
5383     case DP_Pawn:\r
5384       DropMenuEvent(WhitePawn, fromX, fromY);\r
5385       fromX = fromY = -1;\r
5386       break;\r
5387 \r
5388     case DP_Knight:\r
5389       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5390       fromX = fromY = -1;\r
5391       break;\r
5392 \r
5393     case DP_Bishop:\r
5394       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5395       fromX = fromY = -1;\r
5396       break;\r
5397 \r
5398     case DP_Rook:\r
5399       DropMenuEvent(WhiteRook, fromX, fromY);\r
5400       fromX = fromY = -1;\r
5401       break;\r
5402 \r
5403     case DP_Queen:\r
5404       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5405       fromX = fromY = -1;\r
5406       break;\r
5407 \r
5408     case IDM_English:\r
5409       barbaric = 0; appData.language = "";\r
5410       TranslateMenus(0);\r
5411       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5412       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5413       lastChecked = wmId;\r
5414       break;\r
5415 \r
5416     default:\r
5417       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5418           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5419           TranslateMenus(0);\r
5420           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5421           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5422           lastChecked = wmId;\r
5423           break;\r
5424       }\r
5425       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5426     }\r
5427     break;\r
5428 \r
5429   case WM_TIMER:\r
5430     switch (wParam) {\r
5431     case CLOCK_TIMER_ID:\r
5432       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5433       clockTimerEvent = 0;\r
5434       DecrementClocks(); /* call into back end */\r
5435       break;\r
5436     case LOAD_GAME_TIMER_ID:\r
5437       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5438       loadGameTimerEvent = 0;\r
5439       AutoPlayGameLoop(); /* call into back end */\r
5440       break;\r
5441     case ANALYSIS_TIMER_ID:\r
5442       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5443                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5444         AnalysisPeriodicEvent(0);\r
5445       } else {\r
5446         KillTimer(hwnd, analysisTimerEvent);\r
5447         analysisTimerEvent = 0;\r
5448       }\r
5449       break;\r
5450     case DELAYED_TIMER_ID:\r
5451       KillTimer(hwnd, delayedTimerEvent);\r
5452       delayedTimerEvent = 0;\r
5453       delayedTimerCallback();\r
5454       break;\r
5455     }\r
5456     break;\r
5457 \r
5458   case WM_USER_Input:\r
5459     InputEvent(hwnd, message, wParam, lParam);\r
5460     break;\r
5461 \r
5462   /* [AS] Also move "attached" child windows */\r
5463   case WM_WINDOWPOSCHANGING:\r
5464 \r
5465     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5466         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5467 \r
5468         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5469             /* Window is moving */\r
5470             RECT rcMain;\r
5471 \r
5472 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5473             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5474             rcMain.right  = wpMain.x + wpMain.width;\r
5475             rcMain.top    = wpMain.y;\r
5476             rcMain.bottom = wpMain.y + wpMain.height;\r
5477             \r
5478             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5479             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5480             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5481             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5482             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5483             wpMain.x = lpwp->x;\r
5484             wpMain.y = lpwp->y;\r
5485         }\r
5486     }\r
5487     break;\r
5488 \r
5489   /* [AS] Snapping */\r
5490   case WM_ENTERSIZEMOVE:\r
5491     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5492     if (hwnd == hwndMain) {\r
5493       doingSizing = TRUE;\r
5494       lastSizing = 0;\r
5495     }\r
5496     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5497     break;\r
5498 \r
5499   case WM_SIZING:\r
5500     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5501     if (hwnd == hwndMain) {\r
5502       lastSizing = wParam;\r
5503     }\r
5504     break;\r
5505 \r
5506   case WM_MOVING:\r
5507     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5508       return OnMoving( &sd, hwnd, wParam, lParam );\r
5509 \r
5510   case WM_EXITSIZEMOVE:\r
5511     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5512     if (hwnd == hwndMain) {\r
5513       RECT client;\r
5514       doingSizing = FALSE;\r
5515       InvalidateRect(hwnd, &boardRect, FALSE);\r
5516       GetClientRect(hwnd, &client);\r
5517       ResizeBoard(client.right, client.bottom, lastSizing);\r
5518       lastSizing = 0;\r
5519       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5520     }\r
5521     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5522     break;\r
5523 \r
5524   case WM_DESTROY: /* message: window being destroyed */\r
5525     PostQuitMessage(0);\r
5526     break;\r
5527 \r
5528   case WM_CLOSE:\r
5529     if (hwnd == hwndMain) {\r
5530       ExitEvent(0);\r
5531     }\r
5532     break;\r
5533 \r
5534   default:      /* Passes it on if unprocessed */\r
5535     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5536   }\r
5537   return 0;\r
5538 }\r
5539 \r
5540 /*---------------------------------------------------------------------------*\\r
5541  *\r
5542  * Misc utility routines\r
5543  *\r
5544 \*---------------------------------------------------------------------------*/\r
5545 \r
5546 /*\r
5547  * Decent random number generator, at least not as bad as Windows\r
5548  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5549  */\r
5550 unsigned int randstate;\r
5551 \r
5552 int\r
5553 myrandom(void)\r
5554 {\r
5555   randstate = randstate * 1664525 + 1013904223;\r
5556   return (int) randstate & 0x7fffffff;\r
5557 }\r
5558 \r
5559 void\r
5560 mysrandom(unsigned int seed)\r
5561 {\r
5562   randstate = seed;\r
5563 }\r
5564 \r
5565 \r
5566 /* \r
5567  * returns TRUE if user selects a different color, FALSE otherwise \r
5568  */\r
5569 \r
5570 BOOL\r
5571 ChangeColor(HWND hwnd, COLORREF *which)\r
5572 {\r
5573   static BOOL firstTime = TRUE;\r
5574   static DWORD customColors[16];\r
5575   CHOOSECOLOR cc;\r
5576   COLORREF newcolor;\r
5577   int i;\r
5578   ColorClass ccl;\r
5579 \r
5580   if (firstTime) {\r
5581     /* Make initial colors in use available as custom colors */\r
5582     /* Should we put the compiled-in defaults here instead? */\r
5583     i = 0;\r
5584     customColors[i++] = lightSquareColor & 0xffffff;\r
5585     customColors[i++] = darkSquareColor & 0xffffff;\r
5586     customColors[i++] = whitePieceColor & 0xffffff;\r
5587     customColors[i++] = blackPieceColor & 0xffffff;\r
5588     customColors[i++] = highlightSquareColor & 0xffffff;\r
5589     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5590 \r
5591     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5592       customColors[i++] = textAttribs[ccl].color;\r
5593     }\r
5594     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5595     firstTime = FALSE;\r
5596   }\r
5597 \r
5598   cc.lStructSize = sizeof(cc);\r
5599   cc.hwndOwner = hwnd;\r
5600   cc.hInstance = NULL;\r
5601   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5602   cc.lpCustColors = (LPDWORD) customColors;\r
5603   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5604 \r
5605   if (!ChooseColor(&cc)) return FALSE;\r
5606 \r
5607   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5608   if (newcolor == *which) return FALSE;\r
5609   *which = newcolor;\r
5610   return TRUE;\r
5611 \r
5612   /*\r
5613   InitDrawingColors();\r
5614   InvalidateRect(hwnd, &boardRect, FALSE);\r
5615   */\r
5616 }\r
5617 \r
5618 BOOLEAN\r
5619 MyLoadSound(MySound *ms)\r
5620 {\r
5621   BOOL ok = FALSE;\r
5622   struct stat st;\r
5623   FILE *f;\r
5624 \r
5625   if (ms->data && ms->flag) free(ms->data);\r
5626   ms->data = NULL;\r
5627 \r
5628   switch (ms->name[0]) {\r
5629   case NULLCHAR:\r
5630     /* Silence */\r
5631     ok = TRUE;\r
5632     break;\r
5633   case '$':\r
5634     /* System sound from Control Panel.  Don't preload here. */\r
5635     ok = TRUE;\r
5636     break;\r
5637   case '!':\r
5638     if (ms->name[1] == NULLCHAR) {\r
5639       /* "!" alone = silence */\r
5640       ok = TRUE;\r
5641     } else {\r
5642       /* Builtin wave resource.  Error if not found. */\r
5643       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5644       if (h == NULL) break;\r
5645       ms->data = (void *)LoadResource(hInst, h);\r
5646       ms->flag = 0; // not maloced, so cannot be freed!\r
5647       if (h == NULL) break;\r
5648       ok = TRUE;\r
5649     }\r
5650     break;\r
5651   default:\r
5652     /* .wav file.  Error if not found. */\r
5653     f = fopen(ms->name, "rb");\r
5654     if (f == NULL) break;\r
5655     if (fstat(fileno(f), &st) < 0) break;\r
5656     ms->data = malloc(st.st_size);\r
5657     ms->flag = 1;\r
5658     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5659     fclose(f);\r
5660     ok = TRUE;\r
5661     break;\r
5662   }\r
5663   if (!ok) {\r
5664     char buf[MSG_SIZ];\r
5665       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5666     DisplayError(buf, GetLastError());\r
5667   }\r
5668   return ok;\r
5669 }\r
5670 \r
5671 BOOLEAN\r
5672 MyPlaySound(MySound *ms)\r
5673 {\r
5674   BOOLEAN ok = FALSE;\r
5675 \r
5676   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5677   switch (ms->name[0]) {\r
5678   case NULLCHAR:\r
5679         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5680     /* Silence */\r
5681     ok = TRUE;\r
5682     break;\r
5683   case '$':\r
5684     /* System sound from Control Panel (deprecated feature).\r
5685        "$" alone or an unset sound name gets default beep (still in use). */\r
5686     if (ms->name[1]) {\r
5687       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5688     }\r
5689     if (!ok) ok = MessageBeep(MB_OK);\r
5690     break; \r
5691   case '!':\r
5692     /* Builtin wave resource, or "!" alone for silence */\r
5693     if (ms->name[1]) {\r
5694       if (ms->data == NULL) return FALSE;\r
5695       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5696     } else {\r
5697       ok = TRUE;\r
5698     }\r
5699     break;\r
5700   default:\r
5701     /* .wav file.  Error if not found. */\r
5702     if (ms->data == NULL) return FALSE;\r
5703     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5704     break;\r
5705   }\r
5706   /* Don't print an error: this can happen innocently if the sound driver\r
5707      is busy; for instance, if another instance of WinBoard is playing\r
5708      a sound at about the same time. */\r
5709   return ok;\r
5710 }\r
5711 \r
5712 \r
5713 LRESULT CALLBACK\r
5714 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5715 {\r
5716   BOOL ok;\r
5717   OPENFILENAME *ofn;\r
5718   static UINT *number; /* gross that this is static */\r
5719 \r
5720   switch (message) {\r
5721   case WM_INITDIALOG: /* message: initialize dialog box */\r
5722     /* Center the dialog over the application window */\r
5723     ofn = (OPENFILENAME *) lParam;\r
5724     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5725       number = (UINT *) ofn->lCustData;\r
5726       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5727     } else {\r
5728       number = NULL;\r
5729     }\r
5730     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5731     Translate(hDlg, 1536);\r
5732     return FALSE;  /* Allow for further processing */\r
5733 \r
5734   case WM_COMMAND:\r
5735     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5736       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5737     }\r
5738     return FALSE;  /* Allow for further processing */\r
5739   }\r
5740   return FALSE;\r
5741 }\r
5742 \r
5743 UINT APIENTRY\r
5744 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5745 {\r
5746   static UINT *number;\r
5747   OPENFILENAME *ofname;\r
5748   OFNOTIFY *ofnot;\r
5749   switch (uiMsg) {\r
5750   case WM_INITDIALOG:\r
5751     Translate(hdlg, DLG_IndexNumber);\r
5752     ofname = (OPENFILENAME *)lParam;\r
5753     number = (UINT *)(ofname->lCustData);\r
5754     break;\r
5755   case WM_NOTIFY:\r
5756     ofnot = (OFNOTIFY *)lParam;\r
5757     if (ofnot->hdr.code == CDN_FILEOK) {\r
5758       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5759     }\r
5760     break;\r
5761   }\r
5762   return 0;\r
5763 }\r
5764 \r
5765 \r
5766 FILE *\r
5767 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5768                char *nameFilt, char *dlgTitle, UINT *number,\r
5769                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5770 {\r
5771   OPENFILENAME openFileName;\r
5772   char buf1[MSG_SIZ];\r
5773   FILE *f;\r
5774 \r
5775   if (fileName == NULL) fileName = buf1;\r
5776   if (defName == NULL) {\r
5777     safeStrCpy(fileName, "*.", 3 );\r
5778     strcat(fileName, defExt);\r
5779   } else {\r
5780     safeStrCpy(fileName, defName, MSG_SIZ );\r
5781   }\r
5782     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5783   if (number) *number = 0;\r
5784 \r
5785   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5786   openFileName.hwndOwner         = hwnd;\r
5787   openFileName.hInstance         = (HANDLE) hInst;\r
5788   openFileName.lpstrFilter       = nameFilt;\r
5789   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5790   openFileName.nMaxCustFilter    = 0L;\r
5791   openFileName.nFilterIndex      = 1L;\r
5792   openFileName.lpstrFile         = fileName;\r
5793   openFileName.nMaxFile          = MSG_SIZ;\r
5794   openFileName.lpstrFileTitle    = fileTitle;\r
5795   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5796   openFileName.lpstrInitialDir   = NULL;\r
5797   openFileName.lpstrTitle        = dlgTitle;\r
5798   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5799     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5800     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5801     | (oldDialog ? 0 : OFN_EXPLORER);\r
5802   openFileName.nFileOffset       = 0;\r
5803   openFileName.nFileExtension    = 0;\r
5804   openFileName.lpstrDefExt       = defExt;\r
5805   openFileName.lCustData         = (LONG) number;\r
5806   openFileName.lpfnHook          = oldDialog ?\r
5807     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5808   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5809 \r
5810   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5811                         GetOpenFileName(&openFileName)) {\r
5812     /* open the file */\r
5813     f = fopen(openFileName.lpstrFile, write);\r
5814     if (f == NULL) {\r
5815       MessageBox(hwnd, _("File open failed"), NULL,\r
5816                  MB_OK|MB_ICONEXCLAMATION);\r
5817       return NULL;\r
5818     }\r
5819   } else {\r
5820     int err = CommDlgExtendedError();\r
5821     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5822     return FALSE;\r
5823   }\r
5824   return f;\r
5825 }\r
5826 \r
5827 \r
5828 \r
5829 VOID APIENTRY\r
5830 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5831 {\r
5832   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5833 \r
5834   /*\r
5835    * Get the first pop-up menu in the menu template. This is the\r
5836    * menu that TrackPopupMenu displays.\r
5837    */\r
5838   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5839   TranslateOneMenu(10, hmenuTrackPopup);\r
5840 \r
5841   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5842 \r
5843   /*\r
5844    * TrackPopup uses screen coordinates, so convert the\r
5845    * coordinates of the mouse click to screen coordinates.\r
5846    */\r
5847   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5848 \r
5849   /* Draw and track the floating pop-up menu. */\r
5850   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5851                  pt.x, pt.y, 0, hwnd, NULL);\r
5852 \r
5853   /* Destroy the menu.*/\r
5854   DestroyMenu(hmenu);\r
5855 }\r
5856    \r
5857 typedef struct {\r
5858   HWND hDlg, hText;\r
5859   int sizeX, sizeY, newSizeX, newSizeY;\r
5860   HDWP hdwp;\r
5861 } ResizeEditPlusButtonsClosure;\r
5862 \r
5863 BOOL CALLBACK\r
5864 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5865 {\r
5866   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5867   RECT rect;\r
5868   POINT pt;\r
5869 \r
5870   if (hChild == cl->hText) return TRUE;\r
5871   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5872   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5873   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5874   ScreenToClient(cl->hDlg, &pt);\r
5875   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5876     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5877   return TRUE;\r
5878 }\r
5879 \r
5880 /* Resize a dialog that has a (rich) edit field filling most of\r
5881    the top, with a row of buttons below */\r
5882 VOID\r
5883 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5884 {\r
5885   RECT rectText;\r
5886   int newTextHeight, newTextWidth;\r
5887   ResizeEditPlusButtonsClosure cl;\r
5888   \r
5889   /*if (IsIconic(hDlg)) return;*/\r
5890   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5891   \r
5892   cl.hdwp = BeginDeferWindowPos(8);\r
5893 \r
5894   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5895   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5896   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5897   if (newTextHeight < 0) {\r
5898     newSizeY += -newTextHeight;\r
5899     newTextHeight = 0;\r
5900   }\r
5901   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5902     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5903 \r
5904   cl.hDlg = hDlg;\r
5905   cl.hText = hText;\r
5906   cl.sizeX = sizeX;\r
5907   cl.sizeY = sizeY;\r
5908   cl.newSizeX = newSizeX;\r
5909   cl.newSizeY = newSizeY;\r
5910   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5911 \r
5912   EndDeferWindowPos(cl.hdwp);\r
5913 }\r
5914 \r
5915 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5916 {\r
5917     RECT    rChild, rParent;\r
5918     int     wChild, hChild, wParent, hParent;\r
5919     int     wScreen, hScreen, xNew, yNew;\r
5920     HDC     hdc;\r
5921 \r
5922     /* Get the Height and Width of the child window */\r
5923     GetWindowRect (hwndChild, &rChild);\r
5924     wChild = rChild.right - rChild.left;\r
5925     hChild = rChild.bottom - rChild.top;\r
5926 \r
5927     /* Get the Height and Width of the parent window */\r
5928     GetWindowRect (hwndParent, &rParent);\r
5929     wParent = rParent.right - rParent.left;\r
5930     hParent = rParent.bottom - rParent.top;\r
5931 \r
5932     /* Get the display limits */\r
5933     hdc = GetDC (hwndChild);\r
5934     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5935     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5936     ReleaseDC(hwndChild, hdc);\r
5937 \r
5938     /* Calculate new X position, then adjust for screen */\r
5939     xNew = rParent.left + ((wParent - wChild) /2);\r
5940     if (xNew < 0) {\r
5941         xNew = 0;\r
5942     } else if ((xNew+wChild) > wScreen) {\r
5943         xNew = wScreen - wChild;\r
5944     }\r
5945 \r
5946     /* Calculate new Y position, then adjust for screen */\r
5947     if( mode == 0 ) {\r
5948         yNew = rParent.top  + ((hParent - hChild) /2);\r
5949     }\r
5950     else {\r
5951         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5952     }\r
5953 \r
5954     if (yNew < 0) {\r
5955         yNew = 0;\r
5956     } else if ((yNew+hChild) > hScreen) {\r
5957         yNew = hScreen - hChild;\r
5958     }\r
5959 \r
5960     /* Set it, and return */\r
5961     return SetWindowPos (hwndChild, NULL,\r
5962                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5963 }\r
5964 \r
5965 /* Center one window over another */\r
5966 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
5967 {\r
5968     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
5969 }\r
5970 \r
5971 /*---------------------------------------------------------------------------*\\r
5972  *\r
5973  * Startup Dialog functions\r
5974  *\r
5975 \*---------------------------------------------------------------------------*/\r
5976 void\r
5977 InitComboStrings(HANDLE hwndCombo, char **cd)\r
5978 {\r
5979   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5980 \r
5981   while (*cd != NULL) {\r
5982     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
5983     cd++;\r
5984   }\r
5985 }\r
5986 \r
5987 void\r
5988 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
5989 {\r
5990   char buf1[MAX_ARG_LEN];\r
5991   int len;\r
5992 \r
5993   if (str[0] == '@') {\r
5994     FILE* f = fopen(str + 1, "r");\r
5995     if (f == NULL) {\r
5996       DisplayFatalError(str + 1, errno, 2);\r
5997       return;\r
5998     }\r
5999     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6000     fclose(f);\r
6001     buf1[len] = NULLCHAR;\r
6002     str = buf1;\r
6003   }\r
6004 \r
6005   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6006 \r
6007   for (;;) {\r
6008     char buf[MSG_SIZ];\r
6009     char *end = strchr(str, '\n');\r
6010     if (end == NULL) return;\r
6011     memcpy(buf, str, end - str);\r
6012     buf[end - str] = NULLCHAR;\r
6013     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6014     str = end + 1;\r
6015   }\r
6016 }\r
6017 \r
6018 void\r
6019 SetStartupDialogEnables(HWND hDlg)\r
6020 {\r
6021   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6022     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6023     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6024   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6025     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6026   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6027     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6028   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6029     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6030   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6031     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6032     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6033     IsDlgButtonChecked(hDlg, OPT_View));\r
6034 }\r
6035 \r
6036 char *\r
6037 QuoteForFilename(char *filename)\r
6038 {\r
6039   int dquote, space;\r
6040   dquote = strchr(filename, '"') != NULL;\r
6041   space = strchr(filename, ' ') != NULL;\r
6042   if (dquote || space) {\r
6043     if (dquote) {\r
6044       return "'";\r
6045     } else {\r
6046       return "\"";\r
6047     }\r
6048   } else {\r
6049     return "";\r
6050   }\r
6051 }\r
6052 \r
6053 VOID\r
6054 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6055 {\r
6056   char buf[MSG_SIZ];\r
6057   char *q;\r
6058 \r
6059   InitComboStringsFromOption(hwndCombo, nthnames);\r
6060   q = QuoteForFilename(nthcp);\r
6061     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6062   if (*nthdir != NULLCHAR) {\r
6063     q = QuoteForFilename(nthdir);\r
6064       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6065   }\r
6066   if (*nthcp == NULLCHAR) {\r
6067     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6068   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6069     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6070     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6071   }\r
6072 }\r
6073 \r
6074 LRESULT CALLBACK\r
6075 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6076 {\r
6077   char buf[MSG_SIZ];\r
6078   HANDLE hwndCombo;\r
6079   char *p;\r
6080 \r
6081   switch (message) {\r
6082   case WM_INITDIALOG:\r
6083     /* Center the dialog */\r
6084     CenterWindow (hDlg, GetDesktopWindow());\r
6085     Translate(hDlg, DLG_Startup);\r
6086     /* Initialize the dialog items */\r
6087     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6088                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6089                   firstChessProgramNames);\r
6090     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6091                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6092                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6093     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6094     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6095       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6096     if (*appData.icsHelper != NULLCHAR) {\r
6097       char *q = QuoteForFilename(appData.icsHelper);\r
6098       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6099     }\r
6100     if (*appData.icsHost == NULLCHAR) {\r
6101       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6102       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6103     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6104       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6105       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6106     }\r
6107 \r
6108     if (appData.icsActive) {\r
6109       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6110     }\r
6111     else if (appData.noChessProgram) {\r
6112       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6113     }\r
6114     else {\r
6115       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6116     }\r
6117 \r
6118     SetStartupDialogEnables(hDlg);\r
6119     return TRUE;\r
6120 \r
6121   case WM_COMMAND:\r
6122     switch (LOWORD(wParam)) {\r
6123     case IDOK:\r
6124       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6125         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6126         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6127         p = buf;\r
6128         ParseArgs(StringGet, &p);\r
6129         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6130         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6131         p = buf;
6132         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6133         ParseArgs(StringGet, &p);\r
6134         SwapEngines(singleList); // ... and then make it 'second'\r
6135         appData.noChessProgram = FALSE;\r
6136         appData.icsActive = FALSE;\r
6137       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6138         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6139         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6140         p = buf;\r
6141         ParseArgs(StringGet, &p);\r
6142         if (appData.zippyPlay) {\r
6143           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6144           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6145           p = buf;\r
6146           ParseArgs(StringGet, &p);\r
6147         }\r
6148       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6149         appData.noChessProgram = TRUE;\r
6150         appData.icsActive = FALSE;\r
6151       } else {\r
6152         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6153                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6154         return TRUE;\r
6155       }\r
6156       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6157         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6158         p = buf;\r
6159         ParseArgs(StringGet, &p);\r
6160       }\r
6161       EndDialog(hDlg, TRUE);\r
6162       return TRUE;\r
6163 \r
6164     case IDCANCEL:\r
6165       ExitEvent(0);\r
6166       return TRUE;\r
6167 \r
6168     case IDM_HELPCONTENTS:\r
6169       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6170         MessageBox (GetFocus(),\r
6171                     _("Unable to activate help"),\r
6172                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6173       }\r
6174       break;\r
6175 \r
6176     default:\r
6177       SetStartupDialogEnables(hDlg);\r
6178       break;\r
6179     }\r
6180     break;\r
6181   }\r
6182   return FALSE;\r
6183 }\r
6184 \r
6185 /*---------------------------------------------------------------------------*\\r
6186  *\r
6187  * About box dialog functions\r
6188  *\r
6189 \*---------------------------------------------------------------------------*/\r
6190 \r
6191 /* Process messages for "About" dialog box */\r
6192 LRESULT CALLBACK\r
6193 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6194 {\r
6195   switch (message) {\r
6196   case WM_INITDIALOG: /* message: initialize dialog box */\r
6197     /* Center the dialog over the application window */\r
6198     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6199     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6200     Translate(hDlg, ABOUTBOX);\r
6201     JAWS_COPYRIGHT\r
6202     return (TRUE);\r
6203 \r
6204   case WM_COMMAND: /* message: received a command */\r
6205     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6206         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6207       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6208       return (TRUE);\r
6209     }\r
6210     break;\r
6211   }\r
6212   return (FALSE);\r
6213 }\r
6214 \r
6215 /*---------------------------------------------------------------------------*\\r
6216  *\r
6217  * Comment Dialog functions\r
6218  *\r
6219 \*---------------------------------------------------------------------------*/\r
6220 \r
6221 LRESULT CALLBACK\r
6222 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6223 {\r
6224   static HANDLE hwndText = NULL;\r
6225   int len, newSizeX, newSizeY, flags;\r
6226   static int sizeX, sizeY;\r
6227   char *str;\r
6228   RECT rect;\r
6229   MINMAXINFO *mmi;\r
6230 \r
6231   switch (message) {\r
6232   case WM_INITDIALOG: /* message: initialize dialog box */\r
6233     /* Initialize the dialog items */\r
6234     Translate(hDlg, DLG_EditComment);\r
6235     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6236     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6237     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6238     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6239     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6240     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6241     SetWindowText(hDlg, commentTitle);\r
6242     if (editComment) {\r
6243       SetFocus(hwndText);\r
6244     } else {\r
6245       SetFocus(GetDlgItem(hDlg, IDOK));\r
6246     }\r
6247     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6248                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6249                 MAKELPARAM(FALSE, 0));\r
6250     /* Size and position the dialog */\r
6251     if (!commentDialog) {\r
6252       commentDialog = hDlg;\r
6253       flags = SWP_NOZORDER;\r
6254       GetClientRect(hDlg, &rect);\r
6255       sizeX = rect.right;\r
6256       sizeY = rect.bottom;\r
6257       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6258           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6259         WINDOWPLACEMENT wp;\r
6260         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6261         wp.length = sizeof(WINDOWPLACEMENT);\r
6262         wp.flags = 0;\r
6263         wp.showCmd = SW_SHOW;\r
6264         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6265         wp.rcNormalPosition.left = wpComment.x;\r
6266         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6267         wp.rcNormalPosition.top = wpComment.y;\r
6268         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6269         SetWindowPlacement(hDlg, &wp);\r
6270 \r
6271         GetClientRect(hDlg, &rect);\r
6272         newSizeX = rect.right;\r
6273         newSizeY = rect.bottom;\r
6274         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6275                               newSizeX, newSizeY);\r
6276         sizeX = newSizeX;\r
6277         sizeY = newSizeY;\r
6278       }\r
6279     }\r
6280     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6281     return FALSE;\r
6282 \r
6283   case WM_COMMAND: /* message: received a command */\r
6284     switch (LOWORD(wParam)) {\r
6285     case IDOK:\r
6286       if (editComment) {\r
6287         char *p, *q;\r
6288         /* Read changed options from the dialog box */\r
6289         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6290         len = GetWindowTextLength(hwndText);\r
6291         str = (char *) malloc(len + 1);\r
6292         GetWindowText(hwndText, str, len + 1);\r
6293         p = q = str;\r
6294         while (*q) {\r
6295           if (*q == '\r')\r
6296             q++;\r
6297           else\r
6298             *p++ = *q++;\r
6299         }\r
6300         *p = NULLCHAR;\r
6301         ReplaceComment(commentIndex, str);\r
6302         free(str);\r
6303       }\r
6304       CommentPopDown();\r
6305       return TRUE;\r
6306 \r
6307     case IDCANCEL:\r
6308     case OPT_CancelComment:\r
6309       CommentPopDown();\r
6310       return TRUE;\r
6311 \r
6312     case OPT_ClearComment:\r
6313       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6314       break;\r
6315 \r
6316     case OPT_EditComment:\r
6317       EditCommentEvent();\r
6318       return TRUE;\r
6319 \r
6320     default:\r
6321       break;\r
6322     }\r
6323     break;\r
6324 \r
6325   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6326         if( wParam == OPT_CommentText ) {\r
6327             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6328 \r
6329             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6330                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6331                 POINTL pt;\r
6332                 LRESULT index;\r
6333 \r
6334                 pt.x = LOWORD( lpMF->lParam );\r
6335                 pt.y = HIWORD( lpMF->lParam );\r
6336 \r
6337                 if(lpMF->msg == WM_CHAR) {\r
6338                         CHARRANGE sel;\r
6339                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6340                         index = sel.cpMin;\r
6341                 } else\r
6342                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6343 \r
6344                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6345                 len = GetWindowTextLength(hwndText);\r
6346                 str = (char *) malloc(len + 1);\r
6347                 GetWindowText(hwndText, str, len + 1);\r
6348                 ReplaceComment(commentIndex, str);\r
6349                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6350                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6351                 free(str);\r
6352 \r
6353                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6354                 lpMF->msg = WM_USER;\r
6355 \r
6356                 return TRUE;\r
6357             }\r
6358         }\r
6359         break;\r
6360 \r
6361   case WM_SIZE:\r
6362     newSizeX = LOWORD(lParam);\r
6363     newSizeY = HIWORD(lParam);\r
6364     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6365     sizeX = newSizeX;\r
6366     sizeY = newSizeY;\r
6367     break;\r
6368 \r
6369   case WM_GETMINMAXINFO:\r
6370     /* Prevent resizing window too small */\r
6371     mmi = (MINMAXINFO *) lParam;\r
6372     mmi->ptMinTrackSize.x = 100;\r
6373     mmi->ptMinTrackSize.y = 100;\r
6374     break;\r
6375   }\r
6376   return FALSE;\r
6377 }\r
6378 \r
6379 VOID\r
6380 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6381 {\r
6382   FARPROC lpProc;\r
6383   char *p, *q;\r
6384 \r
6385   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6386 \r
6387   if (str == NULL) str = "";\r
6388   p = (char *) malloc(2 * strlen(str) + 2);\r
6389   q = p;\r
6390   while (*str) {\r
6391     if (*str == '\n') *q++ = '\r';\r
6392     *q++ = *str++;\r
6393   }\r
6394   *q = NULLCHAR;\r
6395   if (commentText != NULL) free(commentText);\r
6396 \r
6397   commentIndex = index;\r
6398   commentTitle = title;\r
6399   commentText = p;\r
6400   editComment = edit;\r
6401 \r
6402   if (commentDialog) {\r
6403     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6404     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6405   } else {\r
6406     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6407     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6408                  hwndMain, (DLGPROC)lpProc);\r
6409     FreeProcInstance(lpProc);\r
6410   }\r
6411   commentUp = TRUE;\r
6412 }\r
6413 \r
6414 \r
6415 /*---------------------------------------------------------------------------*\\r
6416  *\r
6417  * Type-in move dialog functions\r
6418  * \r
6419 \*---------------------------------------------------------------------------*/\r
6420 \r
6421 LRESULT CALLBACK\r
6422 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6423 {\r
6424   char move[MSG_SIZ];\r
6425   HWND hInput;\r
6426 \r
6427   switch (message) {\r
6428   case WM_INITDIALOG:\r
6429     move[0] = (char) lParam;\r
6430     move[1] = NULLCHAR;\r
6431     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6432     Translate(hDlg, DLG_TypeInMove);\r
6433     hInput = GetDlgItem(hDlg, OPT_Move);\r
6434     SetWindowText(hInput, move);\r
6435     SetFocus(hInput);\r
6436     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6437     return FALSE;\r
6438 \r
6439   case WM_COMMAND:\r
6440     switch (LOWORD(wParam)) {\r
6441     case IDOK:
6442 \r
6443       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6444       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
6445       TypeInDoneEvent(move);\r
6446       EndDialog(hDlg, TRUE);\r
6447       return TRUE;\r
6448     case IDCANCEL:\r
6449       EndDialog(hDlg, FALSE);\r
6450       return TRUE;\r
6451     default:\r
6452       break;\r
6453     }\r
6454     break;\r
6455   }\r
6456   return FALSE;\r
6457 }\r
6458 \r
6459 VOID\r
6460 PopUpMoveDialog(char firstchar)\r
6461 {\r
6462     FARPROC lpProc;\r
6463 \r
6464       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6465       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6466         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6467       FreeProcInstance(lpProc);\r
6468 }\r
6469 \r
6470 /*---------------------------------------------------------------------------*\\r
6471  *\r
6472  * Type-in name dialog functions\r
6473  * \r
6474 \*---------------------------------------------------------------------------*/\r
6475 \r
6476 LRESULT CALLBACK\r
6477 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6478 {\r
6479   char move[MSG_SIZ];\r
6480   HWND hInput;\r
6481 \r
6482   switch (message) {\r
6483   case WM_INITDIALOG:\r
6484     move[0] = (char) lParam;\r
6485     move[1] = NULLCHAR;\r
6486     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6487     Translate(hDlg, DLG_TypeInName);\r
6488     hInput = GetDlgItem(hDlg, OPT_Name);\r
6489     SetWindowText(hInput, move);\r
6490     SetFocus(hInput);\r
6491     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6492     return FALSE;\r
6493 \r
6494   case WM_COMMAND:\r
6495     switch (LOWORD(wParam)) {\r
6496     case IDOK:\r
6497       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6498       appData.userName = strdup(move);\r
6499       SetUserLogo();\r
6500       SetGameInfo();\r
6501       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6502         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6503         DisplayTitle(move);\r
6504       }\r
6505 \r
6506 \r
6507       EndDialog(hDlg, TRUE);\r
6508       return TRUE;\r
6509     case IDCANCEL:\r
6510       EndDialog(hDlg, FALSE);\r
6511       return TRUE;\r
6512     default:\r
6513       break;\r
6514     }\r
6515     break;\r
6516   }\r
6517   return FALSE;\r
6518 }\r
6519 \r
6520 VOID\r
6521 PopUpNameDialog(char firstchar)\r
6522 {\r
6523     FARPROC lpProc;\r
6524     \r
6525       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6526       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6527         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6528       FreeProcInstance(lpProc);\r
6529 }\r
6530 \r
6531 /*---------------------------------------------------------------------------*\\r
6532  *\r
6533  *  Error dialogs\r
6534  * \r
6535 \*---------------------------------------------------------------------------*/\r
6536 \r
6537 /* Nonmodal error box */\r
6538 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6539                              WPARAM wParam, LPARAM lParam);\r
6540 \r
6541 VOID\r
6542 ErrorPopUp(char *title, char *content)\r
6543 {\r
6544   FARPROC lpProc;\r
6545   char *p, *q;\r
6546   BOOLEAN modal = hwndMain == NULL;\r
6547 \r
6548   p = content;\r
6549   q = errorMessage;\r
6550   while (*p) {\r
6551     if (*p == '\n') {\r
6552       if (modal) {\r
6553         *q++ = ' ';\r
6554         p++;\r
6555       } else {\r
6556         *q++ = '\r';\r
6557         *q++ = *p++;\r
6558       }\r
6559     } else {\r
6560       *q++ = *p++;\r
6561     }\r
6562   }\r
6563   *q = NULLCHAR;\r
6564   strncpy(errorTitle, title, sizeof(errorTitle));\r
6565   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6566   \r
6567   if (modal) {\r
6568     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6569   } else {\r
6570     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6571     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6572                  hwndMain, (DLGPROC)lpProc);\r
6573     FreeProcInstance(lpProc);\r
6574   }\r
6575 }\r
6576 \r
6577 VOID\r
6578 ErrorPopDown()\r
6579 {\r
6580   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6581   if (errorDialog == NULL) return;\r
6582   DestroyWindow(errorDialog);\r
6583   errorDialog = NULL;\r
6584   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6585 }\r
6586 \r
6587 LRESULT CALLBACK\r
6588 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6589 {\r
6590   HANDLE hwndText;\r
6591   RECT rChild;\r
6592 \r
6593   switch (message) {\r
6594   case WM_INITDIALOG:\r
6595     GetWindowRect(hDlg, &rChild);\r
6596 \r
6597     /*\r
6598     SetWindowPos(hDlg, NULL, rChild.left,\r
6599       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6600       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6601     */\r
6602 \r
6603     /* \r
6604         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6605         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6606         and it doesn't work when you resize the dialog.\r
6607         For now, just give it a default position.\r
6608     */\r
6609     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6610     Translate(hDlg, DLG_Error);\r
6611 \r
6612     errorDialog = hDlg;\r
6613     SetWindowText(hDlg, errorTitle);\r
6614     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6615     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6616     return FALSE;\r
6617 \r
6618   case WM_COMMAND:\r
6619     switch (LOWORD(wParam)) {\r
6620     case IDOK:\r
6621     case IDCANCEL:\r
6622       if (errorDialog == hDlg) errorDialog = NULL;\r
6623       DestroyWindow(hDlg);\r
6624       return TRUE;\r
6625 \r
6626     default:\r
6627       break;\r
6628     }\r
6629     break;\r
6630   }\r
6631   return FALSE;\r
6632 }\r
6633 \r
6634 #ifdef GOTHIC\r
6635 HWND gothicDialog = NULL;\r
6636 \r
6637 LRESULT CALLBACK\r
6638 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6639 {\r
6640   HANDLE hwndText;\r
6641   RECT rChild;\r
6642   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6643 \r
6644   switch (message) {\r
6645   case WM_INITDIALOG:\r
6646     GetWindowRect(hDlg, &rChild);\r
6647 \r
6648     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6649                                                              SWP_NOZORDER);\r
6650 \r
6651     /* \r
6652         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6653         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6654         and it doesn't work when you resize the dialog.\r
6655         For now, just give it a default position.\r
6656     */\r
6657     gothicDialog = hDlg;\r
6658     SetWindowText(hDlg, errorTitle);\r
6659     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6660     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6661     return FALSE;\r
6662 \r
6663   case WM_COMMAND:\r
6664     switch (LOWORD(wParam)) {\r
6665     case IDOK:\r
6666     case IDCANCEL:\r
6667       if (errorDialog == hDlg) errorDialog = NULL;\r
6668       DestroyWindow(hDlg);\r
6669       return TRUE;\r
6670 \r
6671     default:\r
6672       break;\r
6673     }\r
6674     break;\r
6675   }\r
6676   return FALSE;\r
6677 }\r
6678 \r
6679 VOID\r
6680 GothicPopUp(char *title, VariantClass variant)\r
6681 {\r
6682   FARPROC lpProc;\r
6683   static char *lastTitle;\r
6684 \r
6685   strncpy(errorTitle, title, sizeof(errorTitle));\r
6686   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6687 \r
6688   if(lastTitle != title && gothicDialog != NULL) {\r
6689     DestroyWindow(gothicDialog);\r
6690     gothicDialog = NULL;\r
6691   }\r
6692   if(variant != VariantNormal && gothicDialog == NULL) {\r
6693     title = lastTitle;\r
6694     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6695     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6696                  hwndMain, (DLGPROC)lpProc);\r
6697     FreeProcInstance(lpProc);\r
6698   }\r
6699 }\r
6700 #endif\r
6701 \r
6702 /*---------------------------------------------------------------------------*\\r
6703  *\r
6704  *  Ics Interaction console functions\r
6705  *\r
6706 \*---------------------------------------------------------------------------*/\r
6707 \r
6708 #define HISTORY_SIZE 64\r
6709 static char *history[HISTORY_SIZE];\r
6710 int histIn = 0, histP = 0;\r
6711 \r
6712 VOID\r
6713 SaveInHistory(char *cmd)\r
6714 {\r
6715   if (history[histIn] != NULL) {\r
6716     free(history[histIn]);\r
6717     history[histIn] = NULL;\r
6718   }\r
6719   if (*cmd == NULLCHAR) return;\r
6720   history[histIn] = StrSave(cmd);\r
6721   histIn = (histIn + 1) % HISTORY_SIZE;\r
6722   if (history[histIn] != NULL) {\r
6723     free(history[histIn]);\r
6724     history[histIn] = NULL;\r
6725   }\r
6726   histP = histIn;\r
6727 }\r
6728 \r
6729 char *\r
6730 PrevInHistory(char *cmd)\r
6731 {\r
6732   int newhp;\r
6733   if (histP == histIn) {\r
6734     if (history[histIn] != NULL) free(history[histIn]);\r
6735     history[histIn] = StrSave(cmd);\r
6736   }\r
6737   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6738   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6739   histP = newhp;\r
6740   return history[histP];\r
6741 }\r
6742 \r
6743 char *\r
6744 NextInHistory()\r
6745 {\r
6746   if (histP == histIn) return NULL;\r
6747   histP = (histP + 1) % HISTORY_SIZE;\r
6748   return history[histP];   \r
6749 }\r
6750 \r
6751 HMENU\r
6752 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6753 {\r
6754   HMENU hmenu, h;\r
6755   int i = 0;\r
6756   hmenu = LoadMenu(hInst, "TextMenu");\r
6757   h = GetSubMenu(hmenu, 0);\r
6758   while (e->item) {\r
6759     if (strcmp(e->item, "-") == 0) {\r
6760       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6761     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6762       int flags = MF_STRING, j = 0;\r
6763       if (e->item[0] == '|') {\r
6764         flags |= MF_MENUBARBREAK;\r
6765         j++;\r
6766       }\r
6767       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6768       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6769     }\r
6770     e++;\r
6771     i++;\r
6772   } \r
6773   return hmenu;\r
6774 }\r
6775 \r
6776 WNDPROC consoleTextWindowProc;\r
6777 \r
6778 void\r
6779 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6780 {\r
6781   char buf[MSG_SIZ], name[MSG_SIZ];\r
6782   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6783   CHARRANGE sel;\r
6784 \r
6785   if (!getname) {\r
6786     SetWindowText(hInput, command);\r
6787     if (immediate) {\r
6788       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6789     } else {\r
6790       sel.cpMin = 999999;\r
6791       sel.cpMax = 999999;\r
6792       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6793       SetFocus(hInput);\r
6794     }\r
6795     return;\r
6796   }    \r
6797   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6798   if (sel.cpMin == sel.cpMax) {\r
6799     /* Expand to surrounding word */\r
6800     TEXTRANGE tr;\r
6801     do {\r
6802       tr.chrg.cpMax = sel.cpMin;\r
6803       tr.chrg.cpMin = --sel.cpMin;\r
6804       if (sel.cpMin < 0) break;\r
6805       tr.lpstrText = name;\r
6806       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6807     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6808     sel.cpMin++;\r
6809 \r
6810     do {\r
6811       tr.chrg.cpMin = sel.cpMax;\r
6812       tr.chrg.cpMax = ++sel.cpMax;\r
6813       tr.lpstrText = name;\r
6814       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6815     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6816     sel.cpMax--;\r
6817 \r
6818     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6819       MessageBeep(MB_ICONEXCLAMATION);\r
6820       return;\r
6821     }\r
6822     tr.chrg = sel;\r
6823     tr.lpstrText = name;\r
6824     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6825   } else {\r
6826     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6827       MessageBeep(MB_ICONEXCLAMATION);\r
6828       return;\r
6829     }\r
6830     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6831   }\r
6832   if (immediate) {\r
6833     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6834     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6835     SetWindowText(hInput, buf);\r
6836     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6837   } else {\r
6838     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6839       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6840     SetWindowText(hInput, buf);\r
6841     sel.cpMin = 999999;\r
6842     sel.cpMax = 999999;\r
6843     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6844     SetFocus(hInput);\r
6845   }\r
6846 }\r
6847 \r
6848 LRESULT CALLBACK \r
6849 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6850 {\r
6851   HWND hInput;\r
6852   CHARRANGE sel;\r
6853 \r
6854   switch (message) {\r
6855   case WM_KEYDOWN:\r
6856     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6857     if(wParam=='R') return 0;\r
6858     switch (wParam) {\r
6859     case VK_PRIOR:\r
6860       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6861       return 0;\r
6862     case VK_NEXT:\r
6863       sel.cpMin = 999999;\r
6864       sel.cpMax = 999999;\r
6865       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6866       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6867       return 0;\r
6868     }\r
6869     break;\r
6870   case WM_CHAR:\r
6871    if(wParam != '\022') {\r
6872     if (wParam == '\t') {\r
6873       if (GetKeyState(VK_SHIFT) < 0) {\r
6874         /* shifted */\r
6875         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6876         if (buttonDesc[0].hwnd) {\r
6877           SetFocus(buttonDesc[0].hwnd);\r
6878         } else {\r
6879           SetFocus(hwndMain);\r
6880         }\r
6881       } else {\r
6882         /* unshifted */\r
6883         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6884       }\r
6885     } else {\r
6886       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6887       JAWS_DELETE( SetFocus(hInput); )\r
6888       SendMessage(hInput, message, wParam, lParam);\r
6889     }\r
6890     return 0;\r
6891    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
6892    lParam = -1;\r
6893   case WM_RBUTTONDOWN:\r
6894     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6895       /* Move selection here if it was empty */\r
6896       POINT pt;\r
6897       pt.x = LOWORD(lParam);\r
6898       pt.y = HIWORD(lParam);\r
6899       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6900       if (sel.cpMin == sel.cpMax) {\r
6901         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6902         sel.cpMax = sel.cpMin;\r
6903         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6904       }\r
6905       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6906 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6907       POINT pt;\r
6908       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6909       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6910       if (sel.cpMin == sel.cpMax) {\r
6911         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6912         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6913       }\r
6914       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6915         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6916       }\r
6917       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6918       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6919       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6920       MenuPopup(hwnd, pt, hmenu, -1);\r
6921 }\r
6922     }\r
6923     return 0;\r
6924   case WM_RBUTTONUP:\r
6925     if (GetKeyState(VK_SHIFT) & ~1) {\r
6926       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6927         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6928     }\r
6929     return 0;\r
6930   case WM_PASTE:\r
6931     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6932     SetFocus(hInput);\r
6933     return SendMessage(hInput, message, wParam, lParam);\r
6934   case WM_MBUTTONDOWN:\r
6935     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6936   case WM_COMMAND:\r
6937     switch (LOWORD(wParam)) {\r
6938     case IDM_QuickPaste:\r
6939       {\r
6940         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6941         if (sel.cpMin == sel.cpMax) {\r
6942           MessageBeep(MB_ICONEXCLAMATION);\r
6943           return 0;\r
6944         }\r
6945         SendMessage(hwnd, WM_COPY, 0, 0);\r
6946         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6947         SendMessage(hInput, WM_PASTE, 0, 0);\r
6948         SetFocus(hInput);\r
6949         return 0;\r
6950       }\r
6951     case IDM_Cut:\r
6952       SendMessage(hwnd, WM_CUT, 0, 0);\r
6953       return 0;\r
6954     case IDM_Paste:\r
6955       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6956       return 0;\r
6957     case IDM_Copy:\r
6958       SendMessage(hwnd, WM_COPY, 0, 0);\r
6959       return 0;\r
6960     default:\r
6961       {\r
6962         int i = LOWORD(wParam) - IDM_CommandX;\r
6963         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6964             icsTextMenuEntry[i].command != NULL) {\r
6965           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6966                    icsTextMenuEntry[i].getname,\r
6967                    icsTextMenuEntry[i].immediate);\r
6968           return 0;\r
6969         }\r
6970       }\r
6971       break;\r
6972     }\r
6973     break;\r
6974   }\r
6975   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
6976 }\r
6977 \r
6978 WNDPROC consoleInputWindowProc;\r
6979 \r
6980 LRESULT CALLBACK\r
6981 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6982 {\r
6983   char buf[MSG_SIZ];\r
6984   char *p;\r
6985   static BOOL sendNextChar = FALSE;\r
6986   static BOOL quoteNextChar = FALSE;\r
6987   InputSource *is = consoleInputSource;\r
6988   CHARFORMAT cf;\r
6989   CHARRANGE sel;\r
6990 \r
6991   switch (message) {\r
6992   case WM_CHAR:\r
6993     if (!appData.localLineEditing || sendNextChar) {\r
6994       is->buf[0] = (CHAR) wParam;\r
6995       is->count = 1;\r
6996       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6997       sendNextChar = FALSE;\r
6998       return 0;\r
6999     }\r
7000     if (quoteNextChar) {\r
7001       buf[0] = (char) wParam;\r
7002       buf[1] = NULLCHAR;\r
7003       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7004       quoteNextChar = FALSE;\r
7005       return 0;\r
7006     }\r
7007     switch (wParam) {\r
7008     case '\r':   /* Enter key */\r
7009       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7010       if (consoleEcho) SaveInHistory(is->buf);\r
7011       is->buf[is->count++] = '\n';\r
7012       is->buf[is->count] = NULLCHAR;\r
7013       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7014       if (consoleEcho) {\r
7015         ConsoleOutput(is->buf, is->count, TRUE);\r
7016       } else if (appData.localLineEditing) {\r
7017         ConsoleOutput("\n", 1, TRUE);\r
7018       }\r
7019       /* fall thru */\r
7020     case '\033': /* Escape key */\r
7021       SetWindowText(hwnd, "");\r
7022       cf.cbSize = sizeof(CHARFORMAT);\r
7023       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7024       if (consoleEcho) {\r
7025         cf.crTextColor = textAttribs[ColorNormal].color;\r
7026       } else {\r
7027         cf.crTextColor = COLOR_ECHOOFF;\r
7028       }\r
7029       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7030       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7031       return 0;\r
7032     case '\t':   /* Tab key */\r
7033       if (GetKeyState(VK_SHIFT) < 0) {\r
7034         /* shifted */\r
7035         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7036       } else {\r
7037         /* unshifted */\r
7038         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7039         if (buttonDesc[0].hwnd) {\r
7040           SetFocus(buttonDesc[0].hwnd);\r
7041         } else {\r
7042           SetFocus(hwndMain);\r
7043         }\r
7044       }\r
7045       return 0;\r
7046     case '\023': /* Ctrl+S */\r
7047       sendNextChar = TRUE;\r
7048       return 0;\r
7049     case '\021': /* Ctrl+Q */\r
7050       quoteNextChar = TRUE;\r
7051       return 0;\r
7052     JAWS_REPLAY\r
7053     default:\r
7054       break;\r
7055     }\r
7056     break;\r
7057   case WM_KEYDOWN:\r
7058     switch (wParam) {\r
7059     case VK_UP:\r
7060       GetWindowText(hwnd, buf, MSG_SIZ);\r
7061       p = PrevInHistory(buf);\r
7062       if (p != NULL) {\r
7063         SetWindowText(hwnd, p);\r
7064         sel.cpMin = 999999;\r
7065         sel.cpMax = 999999;\r
7066         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7067         return 0;\r
7068       }\r
7069       break;\r
7070     case VK_DOWN:\r
7071       p = NextInHistory();\r
7072       if (p != NULL) {\r
7073         SetWindowText(hwnd, p);\r
7074         sel.cpMin = 999999;\r
7075         sel.cpMax = 999999;\r
7076         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7077         return 0;\r
7078       }\r
7079       break;\r
7080     case VK_HOME:\r
7081     case VK_END:\r
7082       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7083       /* fall thru */\r
7084     case VK_PRIOR:\r
7085     case VK_NEXT:\r
7086       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7087       return 0;\r
7088     }\r
7089     break;\r
7090   case WM_MBUTTONDOWN:\r
7091     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7092       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7093     break;\r
7094   case WM_RBUTTONUP:\r
7095     if (GetKeyState(VK_SHIFT) & ~1) {\r
7096       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7097         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7098     } else {\r
7099       POINT pt;\r
7100       HMENU hmenu;\r
7101       hmenu = LoadMenu(hInst, "InputMenu");\r
7102       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7103       if (sel.cpMin == sel.cpMax) {\r
7104         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7105         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7106       }\r
7107       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7108         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7109       }\r
7110       pt.x = LOWORD(lParam);\r
7111       pt.y = HIWORD(lParam);\r
7112       MenuPopup(hwnd, pt, hmenu, -1);\r
7113     }\r
7114     return 0;\r
7115   case WM_COMMAND:\r
7116     switch (LOWORD(wParam)) { \r
7117     case IDM_Undo:\r
7118       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7119       return 0;\r
7120     case IDM_SelectAll:\r
7121       sel.cpMin = 0;\r
7122       sel.cpMax = -1; /*999999?*/\r
7123       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7124       return 0;\r
7125     case IDM_Cut:\r
7126       SendMessage(hwnd, WM_CUT, 0, 0);\r
7127       return 0;\r
7128     case IDM_Paste:\r
7129       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7130       return 0;\r
7131     case IDM_Copy:\r
7132       SendMessage(hwnd, WM_COPY, 0, 0);\r
7133       return 0;\r
7134     }\r
7135     break;\r
7136   }\r
7137   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7138 }\r
7139 \r
7140 #define CO_MAX  100000\r
7141 #define CO_TRIM   1000\r
7142 \r
7143 LRESULT CALLBACK\r
7144 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7145 {\r
7146   static SnapData sd;\r
7147   HWND hText, hInput;\r
7148   RECT rect;\r
7149   static int sizeX, sizeY;\r
7150   int newSizeX, newSizeY;\r
7151   MINMAXINFO *mmi;\r
7152   WORD wMask;\r
7153 \r
7154   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7155   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7156 \r
7157   switch (message) {\r
7158   case WM_NOTIFY:\r
7159     if (((NMHDR*)lParam)->code == EN_LINK)\r
7160     {\r
7161       ENLINK *pLink = (ENLINK*)lParam;\r
7162       if (pLink->msg == WM_LBUTTONUP)\r
7163       {\r
7164         TEXTRANGE tr;\r
7165 \r
7166         tr.chrg = pLink->chrg;\r
7167         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7168         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7169         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7170         free(tr.lpstrText);\r
7171       }\r
7172     }\r
7173     break;\r
7174   case WM_INITDIALOG: /* message: initialize dialog box */\r
7175     hwndConsole = hDlg;\r
7176     SetFocus(hInput);\r
7177     consoleTextWindowProc = (WNDPROC)\r
7178       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7179     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7180     consoleInputWindowProc = (WNDPROC)\r
7181       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7182     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7183     Colorize(ColorNormal, TRUE);\r
7184     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7185     ChangedConsoleFont();\r
7186     GetClientRect(hDlg, &rect);\r
7187     sizeX = rect.right;\r
7188     sizeY = rect.bottom;\r
7189     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7190         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7191       WINDOWPLACEMENT wp;\r
7192       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7193       wp.length = sizeof(WINDOWPLACEMENT);\r
7194       wp.flags = 0;\r
7195       wp.showCmd = SW_SHOW;\r
7196       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7197       wp.rcNormalPosition.left = wpConsole.x;\r
7198       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7199       wp.rcNormalPosition.top = wpConsole.y;\r
7200       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7201       SetWindowPlacement(hDlg, &wp);\r
7202     }\r
7203 \r
7204    // [HGM] Chessknight's change 2004-07-13\r
7205    else { /* Determine Defaults */\r
7206        WINDOWPLACEMENT wp;\r
7207        wpConsole.x = wpMain.width + 1;\r
7208        wpConsole.y = wpMain.y;\r
7209        wpConsole.width = screenWidth -  wpMain.width;\r
7210        wpConsole.height = wpMain.height;\r
7211        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7212        wp.length = sizeof(WINDOWPLACEMENT);\r
7213        wp.flags = 0;\r
7214        wp.showCmd = SW_SHOW;\r
7215        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7216        wp.rcNormalPosition.left = wpConsole.x;\r
7217        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7218        wp.rcNormalPosition.top = wpConsole.y;\r
7219        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7220        SetWindowPlacement(hDlg, &wp);\r
7221     }\r
7222 \r
7223    // Allow hText to highlight URLs and send notifications on them\r
7224    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7225    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7226    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7227    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7228 \r
7229     return FALSE;\r
7230 \r
7231   case WM_SETFOCUS:\r
7232     SetFocus(hInput);\r
7233     return 0;\r
7234 \r
7235   case WM_CLOSE:\r
7236     ExitEvent(0);\r
7237     /* not reached */\r
7238     break;\r
7239 \r
7240   case WM_SIZE:\r
7241     if (IsIconic(hDlg)) break;\r
7242     newSizeX = LOWORD(lParam);\r
7243     newSizeY = HIWORD(lParam);\r
7244     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7245       RECT rectText, rectInput;\r
7246       POINT pt;\r
7247       int newTextHeight, newTextWidth;\r
7248       GetWindowRect(hText, &rectText);\r
7249       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7250       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7251       if (newTextHeight < 0) {\r
7252         newSizeY += -newTextHeight;\r
7253         newTextHeight = 0;\r
7254       }\r
7255       SetWindowPos(hText, NULL, 0, 0,\r
7256         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7257       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7258       pt.x = rectInput.left;\r
7259       pt.y = rectInput.top + newSizeY - sizeY;\r
7260       ScreenToClient(hDlg, &pt);\r
7261       SetWindowPos(hInput, NULL, \r
7262         pt.x, pt.y, /* needs client coords */   \r
7263         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7264         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7265     }\r
7266     sizeX = newSizeX;\r
7267     sizeY = newSizeY;\r
7268     break;\r
7269 \r
7270   case WM_GETMINMAXINFO:\r
7271     /* Prevent resizing window too small */\r
7272     mmi = (MINMAXINFO *) lParam;\r
7273     mmi->ptMinTrackSize.x = 100;\r
7274     mmi->ptMinTrackSize.y = 100;\r
7275     break;\r
7276 \r
7277   /* [AS] Snapping */\r
7278   case WM_ENTERSIZEMOVE:\r
7279     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7280 \r
7281   case WM_SIZING:\r
7282     return OnSizing( &sd, hDlg, wParam, lParam );\r
7283 \r
7284   case WM_MOVING:\r
7285     return OnMoving( &sd, hDlg, wParam, lParam );\r
7286 \r
7287   case WM_EXITSIZEMOVE:\r
7288         UpdateICSWidth(hText);\r
7289     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7290   }\r
7291 \r
7292   return DefWindowProc(hDlg, message, wParam, lParam);\r
7293 }\r
7294 \r
7295 \r
7296 VOID\r
7297 ConsoleCreate()\r
7298 {\r
7299   HWND hCons;\r
7300   if (hwndConsole) return;\r
7301   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7302   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7303 }\r
7304 \r
7305 \r
7306 VOID\r
7307 ConsoleOutput(char* data, int length, int forceVisible)\r
7308 {\r
7309   HWND hText;\r
7310   int trim, exlen;\r
7311   char *p, *q;\r
7312   char buf[CO_MAX+1];\r
7313   POINT pEnd;\r
7314   RECT rect;\r
7315   static int delayLF = 0;\r
7316   CHARRANGE savesel, sel;\r
7317 \r
7318   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7319   p = data;\r
7320   q = buf;\r
7321   if (delayLF) {\r
7322     *q++ = '\r';\r
7323     *q++ = '\n';\r
7324     delayLF = 0;\r
7325   }\r
7326   while (length--) {\r
7327     if (*p == '\n') {\r
7328       if (*++p) {\r
7329         *q++ = '\r';\r
7330         *q++ = '\n';\r
7331       } else {\r
7332         delayLF = 1;\r
7333       }\r
7334     } else if (*p == '\007') {\r
7335        MyPlaySound(&sounds[(int)SoundBell]);\r
7336        p++;\r
7337     } else {\r
7338       *q++ = *p++;\r
7339     }\r
7340   }\r
7341   *q = NULLCHAR;\r
7342   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7343   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7344   /* Save current selection */\r
7345   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7346   exlen = GetWindowTextLength(hText);\r
7347   /* Find out whether current end of text is visible */\r
7348   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7349   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7350   /* Trim existing text if it's too long */\r
7351   if (exlen + (q - buf) > CO_MAX) {\r
7352     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7353     sel.cpMin = 0;\r
7354     sel.cpMax = trim;\r
7355     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7356     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7357     exlen -= trim;\r
7358     savesel.cpMin -= trim;\r
7359     savesel.cpMax -= trim;\r
7360     if (exlen < 0) exlen = 0;\r
7361     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7362     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7363   }\r
7364   /* Append the new text */\r
7365   sel.cpMin = exlen;\r
7366   sel.cpMax = exlen;\r
7367   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7368   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7369   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7370   if (forceVisible || exlen == 0 ||\r
7371       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7372        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7373     /* Scroll to make new end of text visible if old end of text\r
7374        was visible or new text is an echo of user typein */\r
7375     sel.cpMin = 9999999;\r
7376     sel.cpMax = 9999999;\r
7377     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7378     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7379     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7380     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7381   }\r
7382   if (savesel.cpMax == exlen || forceVisible) {\r
7383     /* Move insert point to new end of text if it was at the old\r
7384        end of text or if the new text is an echo of user typein */\r
7385     sel.cpMin = 9999999;\r
7386     sel.cpMax = 9999999;\r
7387     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7388   } else {\r
7389     /* Restore previous selection */\r
7390     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7391   }\r
7392   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7393 }\r
7394 \r
7395 /*---------*/\r
7396 \r
7397 \r
7398 void\r
7399 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7400 {\r
7401   char buf[100];\r
7402   char *str;\r
7403   COLORREF oldFg, oldBg;\r
7404   HFONT oldFont;\r
7405   RECT rect;\r
7406 \r
7407   if(copyNumber > 1)
7408     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7409 \r
7410   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7411   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7412   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7413 \r
7414   rect.left = x;\r
7415   rect.right = x + squareSize;\r
7416   rect.top  = y;\r
7417   rect.bottom = y + squareSize;\r
7418   str = buf;\r
7419 \r
7420   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7421                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7422              y, ETO_CLIPPED|ETO_OPAQUE,\r
7423              &rect, str, strlen(str), NULL);\r
7424 \r
7425   (void) SetTextColor(hdc, oldFg);\r
7426   (void) SetBkColor(hdc, oldBg);\r
7427   (void) SelectObject(hdc, oldFont);\r
7428 }\r
7429 \r
7430 void\r
7431 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7432               RECT *rect, char *color, char *flagFell)\r
7433 {\r
7434   char buf[100];\r
7435   char *str;\r
7436   COLORREF oldFg, oldBg;\r
7437   HFONT oldFont;\r
7438 \r
7439   if (appData.clockMode) {\r
7440     if (tinyLayout)\r
7441       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7442     else\r
7443       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7444     str = buf;\r
7445   } else {\r
7446     str = color;\r
7447   }\r
7448 \r
7449   if (highlight) {\r
7450     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7451     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7452   } else {\r
7453     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7454     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7455   }\r
7456   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7457 \r
7458   JAWS_SILENCE\r
7459 \r
7460   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7461              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7462              rect, str, strlen(str), NULL);\r
7463   if(logoHeight > 0 && appData.clockMode) {\r
7464       RECT r;\r
7465       str += strlen(color)+2;\r
7466       r.top = rect->top + logoHeight/2;\r
7467       r.left = rect->left;\r
7468       r.right = rect->right;\r
7469       r.bottom = rect->bottom;\r
7470       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7471                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7472                  &r, str, strlen(str), NULL);\r
7473   }\r
7474   (void) SetTextColor(hdc, oldFg);\r
7475   (void) SetBkColor(hdc, oldBg);\r
7476   (void) SelectObject(hdc, oldFont);\r
7477 }\r
7478 \r
7479 \r
7480 int\r
7481 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7482            OVERLAPPED *ovl)\r
7483 {\r
7484   int ok, err;\r
7485 \r
7486   /* [AS]  */\r
7487   if( count <= 0 ) {\r
7488     if (appData.debugMode) {\r
7489       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7490     }\r
7491 \r
7492     return ERROR_INVALID_USER_BUFFER;\r
7493   }\r
7494 \r
7495   ResetEvent(ovl->hEvent);\r
7496   ovl->Offset = ovl->OffsetHigh = 0;\r
7497   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7498   if (ok) {\r
7499     err = NO_ERROR;\r
7500   } else {\r
7501     err = GetLastError();\r
7502     if (err == ERROR_IO_PENDING) {\r
7503       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7504       if (ok)\r
7505         err = NO_ERROR;\r
7506       else\r
7507         err = GetLastError();\r
7508     }\r
7509   }\r
7510   return err;\r
7511 }\r
7512 \r
7513 int\r
7514 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7515             OVERLAPPED *ovl)\r
7516 {\r
7517   int ok, err;\r
7518 \r
7519   ResetEvent(ovl->hEvent);\r
7520   ovl->Offset = ovl->OffsetHigh = 0;\r
7521   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7522   if (ok) {\r
7523     err = NO_ERROR;\r
7524   } else {\r
7525     err = GetLastError();\r
7526     if (err == ERROR_IO_PENDING) {\r
7527       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7528       if (ok)\r
7529         err = NO_ERROR;\r
7530       else\r
7531         err = GetLastError();\r
7532     }\r
7533   }\r
7534   return err;\r
7535 }\r
7536 \r
7537 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7538 void CheckForInputBufferFull( InputSource * is )\r
7539 {\r
7540     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7541         /* Look for end of line */\r
7542         char * p = is->buf;\r
7543         \r
7544         while( p < is->next && *p != '\n' ) {\r
7545             p++;\r
7546         }\r
7547 \r
7548         if( p >= is->next ) {\r
7549             if (appData.debugMode) {\r
7550                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7551             }\r
7552 \r
7553             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7554             is->count = (DWORD) -1;\r
7555             is->next = is->buf;\r
7556         }\r
7557     }\r
7558 }\r
7559 \r
7560 DWORD\r
7561 InputThread(LPVOID arg)\r
7562 {\r
7563   InputSource *is;\r
7564   OVERLAPPED ovl;\r
7565 \r
7566   is = (InputSource *) arg;\r
7567   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7568   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7569   while (is->hThread != NULL) {\r
7570     is->error = DoReadFile(is->hFile, is->next,\r
7571                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7572                            &is->count, &ovl);\r
7573     if (is->error == NO_ERROR) {\r
7574       is->next += is->count;\r
7575     } else {\r
7576       if (is->error == ERROR_BROKEN_PIPE) {\r
7577         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7578         is->count = 0;\r
7579       } else {\r
7580         is->count = (DWORD) -1;\r
7581         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7582         break; \r
7583       }\r
7584     }\r
7585 \r
7586     CheckForInputBufferFull( is );\r
7587 \r
7588     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7589 \r
7590     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7591 \r
7592     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7593   }\r
7594 \r
7595   CloseHandle(ovl.hEvent);\r
7596   CloseHandle(is->hFile);\r
7597 \r
7598   if (appData.debugMode) {\r
7599     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7600   }\r
7601 \r
7602   return 0;\r
7603 }\r
7604 \r
7605 \r
7606 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7607 DWORD\r
7608 NonOvlInputThread(LPVOID arg)\r
7609 {\r
7610   InputSource *is;\r
7611   char *p, *q;\r
7612   int i;\r
7613   char prev;\r
7614 \r
7615   is = (InputSource *) arg;\r
7616   while (is->hThread != NULL) {\r
7617     is->error = ReadFile(is->hFile, is->next,\r
7618                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7619                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7620     if (is->error == NO_ERROR) {\r
7621       /* Change CRLF to LF */\r
7622       if (is->next > is->buf) {\r
7623         p = is->next - 1;\r
7624         i = is->count + 1;\r
7625       } else {\r
7626         p = is->next;\r
7627         i = is->count;\r
7628       }\r
7629       q = p;\r
7630       prev = NULLCHAR;\r
7631       while (i > 0) {\r
7632         if (prev == '\r' && *p == '\n') {\r
7633           *(q-1) = '\n';\r
7634           is->count--;\r
7635         } else { \r
7636           *q++ = *p;\r
7637         }\r
7638         prev = *p++;\r
7639         i--;\r
7640       }\r
7641       *q = NULLCHAR;\r
7642       is->next = q;\r
7643     } else {\r
7644       if (is->error == ERROR_BROKEN_PIPE) {\r
7645         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7646         is->count = 0; \r
7647       } else {\r
7648         is->count = (DWORD) -1;\r
7649       }\r
7650     }\r
7651 \r
7652     CheckForInputBufferFull( is );\r
7653 \r
7654     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7655 \r
7656     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7657 \r
7658     if (is->count < 0) break;  /* Quit on error */\r
7659   }\r
7660   CloseHandle(is->hFile);\r
7661   return 0;\r
7662 }\r
7663 \r
7664 DWORD\r
7665 SocketInputThread(LPVOID arg)\r
7666 {\r
7667   InputSource *is;\r
7668 \r
7669   is = (InputSource *) arg;\r
7670   while (is->hThread != NULL) {\r
7671     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7672     if ((int)is->count == SOCKET_ERROR) {\r
7673       is->count = (DWORD) -1;\r
7674       is->error = WSAGetLastError();\r
7675     } else {\r
7676       is->error = NO_ERROR;\r
7677       is->next += is->count;\r
7678       if (is->count == 0 && is->second == is) {\r
7679         /* End of file on stderr; quit with no message */\r
7680         break;\r
7681       }\r
7682     }\r
7683     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7684 \r
7685     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7686 \r
7687     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7688   }\r
7689   return 0;\r
7690 }\r
7691 \r
7692 VOID\r
7693 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7694 {\r
7695   InputSource *is;\r
7696 \r
7697   is = (InputSource *) lParam;\r
7698   if (is->lineByLine) {\r
7699     /* Feed in lines one by one */\r
7700     char *p = is->buf;\r
7701     char *q = p;\r
7702     while (q < is->next) {\r
7703       if (*q++ == '\n') {\r
7704         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7705         p = q;\r
7706       }\r
7707     }\r
7708     \r
7709     /* Move any partial line to the start of the buffer */\r
7710     q = is->buf;\r
7711     while (p < is->next) {\r
7712       *q++ = *p++;\r
7713     }\r
7714     is->next = q;\r
7715 \r
7716     if (is->error != NO_ERROR || is->count == 0) {\r
7717       /* Notify backend of the error.  Note: If there was a partial\r
7718          line at the end, it is not flushed through. */\r
7719       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7720     }\r
7721   } else {\r
7722     /* Feed in the whole chunk of input at once */\r
7723     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7724     is->next = is->buf;\r
7725   }\r
7726 }\r
7727 \r
7728 /*---------------------------------------------------------------------------*\\r
7729  *\r
7730  *  Menu enables. Used when setting various modes.\r
7731  *\r
7732 \*---------------------------------------------------------------------------*/\r
7733 \r
7734 typedef struct {\r
7735   int item;\r
7736   int flags;\r
7737 } Enables;\r
7738 \r
7739 VOID\r
7740 GreyRevert(Boolean grey)\r
7741 { // [HGM] vari: for retracting variations in local mode\r
7742   HMENU hmenu = GetMenu(hwndMain);\r
7743   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7744   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7745 }\r
7746 \r
7747 VOID\r
7748 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7749 {\r
7750   while (enab->item > 0) {\r
7751     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7752     enab++;\r
7753   }\r
7754 }\r
7755 \r
7756 Enables gnuEnables[] = {\r
7757   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7758   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7759   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7760   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7761   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7762   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7763   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7764   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7765   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7766   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7767   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7768   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7769   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7770 \r
7771   // Needed to switch from ncp to GNU mode on Engine Load\r
7772   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7773   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7774   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7775   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7776   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7777   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7778   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7779   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7780   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7781   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7782   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7783   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7784   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7785   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7786   { -1, -1 }\r
7787 };\r
7788 \r
7789 Enables icsEnables[] = {\r
7790   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7791   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7792   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7793   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7794   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7795   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7796   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7797   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7798   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7799   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7800   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7801   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7802   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7803   { IDM_EditProgs2, 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   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
7809   { -1, -1 }\r
7810 };\r
7811 \r
7812 #if ZIPPY\r
7813 Enables zippyEnables[] = {\r
7814   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7815   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7816   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7817   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7818   { -1, -1 }\r
7819 };\r
7820 #endif\r
7821 \r
7822 Enables ncpEnables[] = {\r
7823   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7824   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7825   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7826   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7827   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7828   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7829   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7830   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7831   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7832   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7833   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7834   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7835   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7836   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7837   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7838   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7839   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7840   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7841   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7842   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7843   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7844   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7845   { -1, -1 }\r
7846 };\r
7847 \r
7848 Enables trainingOnEnables[] = {\r
7849   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7850   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7851   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7852   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7853   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7854   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7855   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7856   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7857   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7858   { -1, -1 }\r
7859 };\r
7860 \r
7861 Enables trainingOffEnables[] = {\r
7862   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7863   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7864   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7865   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7866   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7867   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7868   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7869   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7870   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7871   { -1, -1 }\r
7872 };\r
7873 \r
7874 /* These modify either ncpEnables or gnuEnables */\r
7875 Enables cmailEnables[] = {\r
7876   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7877   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7878   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7879   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7880   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7881   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7882   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7883   { -1, -1 }\r
7884 };\r
7885 \r
7886 Enables machineThinkingEnables[] = {\r
7887   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7888   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7889   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7890   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7891   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7892   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7893   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7894   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7895   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7896   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7897   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7898   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7899   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7900 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7901   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7902   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7903   { -1, -1 }\r
7904 };\r
7905 \r
7906 Enables userThinkingEnables[] = {\r
7907   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7908   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7909   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7910   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7911   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7912   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7913   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7914   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7915   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7916   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7917   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7918   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7919   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7920 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7921   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7922   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7923   { -1, -1 }\r
7924 };\r
7925 \r
7926 /*---------------------------------------------------------------------------*\\r
7927  *\r
7928  *  Front-end interface functions exported by XBoard.\r
7929  *  Functions appear in same order as prototypes in frontend.h.\r
7930  * \r
7931 \*---------------------------------------------------------------------------*/\r
7932 VOID\r
7933 CheckMark(UINT item, int state)\r
7934 {\r
7935     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
7936 }\r
7937 \r
7938 VOID\r
7939 ModeHighlight()\r
7940 {\r
7941   static UINT prevChecked = 0;\r
7942   static int prevPausing = 0;\r
7943   UINT nowChecked;\r
7944 \r
7945   if (pausing != prevPausing) {\r
7946     prevPausing = pausing;\r
7947     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7948                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7949     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7950   }\r
7951 \r
7952   switch (gameMode) {\r
7953   case BeginningOfGame:\r
7954     if (appData.icsActive)\r
7955       nowChecked = IDM_IcsClient;\r
7956     else if (appData.noChessProgram)\r
7957       nowChecked = IDM_EditGame;\r
7958     else\r
7959       nowChecked = IDM_MachineBlack;\r
7960     break;\r
7961   case MachinePlaysBlack:\r
7962     nowChecked = IDM_MachineBlack;\r
7963     break;\r
7964   case MachinePlaysWhite:\r
7965     nowChecked = IDM_MachineWhite;\r
7966     break;\r
7967   case TwoMachinesPlay:\r
7968     nowChecked = IDM_TwoMachines;\r
7969     break;\r
7970   case AnalyzeMode:\r
7971     nowChecked = IDM_AnalysisMode;\r
7972     break;\r
7973   case AnalyzeFile:\r
7974     nowChecked = IDM_AnalyzeFile;\r
7975     break;\r
7976   case EditGame:\r
7977     nowChecked = IDM_EditGame;\r
7978     break;\r
7979   case PlayFromGameFile:\r
7980     nowChecked = IDM_LoadGame;\r
7981     break;\r
7982   case EditPosition:\r
7983     nowChecked = IDM_EditPosition;\r
7984     break;\r
7985   case Training:\r
7986     nowChecked = IDM_Training;\r
7987     break;\r
7988   case IcsPlayingWhite:\r
7989   case IcsPlayingBlack:\r
7990   case IcsObserving:\r
7991   case IcsIdle:\r
7992     nowChecked = IDM_IcsClient;\r
7993     break;\r
7994   default:\r
7995   case EndOfGame:\r
7996     nowChecked = 0;\r
7997     break;\r
7998   }\r
7999   CheckMark(prevChecked, MF_UNCHECKED);\r
8000   CheckMark(nowChecked, MF_CHECKED);\r
8001   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8002 \r
8003   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8004     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8005                           MF_BYCOMMAND|MF_ENABLED);\r
8006   } else {\r
8007     (void) EnableMenuItem(GetMenu(hwndMain), \r
8008                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8009   }\r
8010 \r
8011   prevChecked = nowChecked;\r
8012 \r
8013   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8014   if (appData.icsActive) {\r
8015        if (appData.icsEngineAnalyze) {\r
8016                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8017        } else {\r
8018                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8019        }\r
8020   }\r
8021   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8022 }\r
8023 \r
8024 VOID\r
8025 SetICSMode()\r
8026 {\r
8027   HMENU hmenu = GetMenu(hwndMain);\r
8028   SetMenuEnables(hmenu, icsEnables);\r
8029   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8030     MF_BYCOMMAND|MF_ENABLED);\r
8031 #if ZIPPY\r
8032   if (appData.zippyPlay) {\r
8033     SetMenuEnables(hmenu, zippyEnables);\r
8034     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8035          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8036           MF_BYCOMMAND|MF_ENABLED);\r
8037   }\r
8038 #endif\r
8039 }\r
8040 \r
8041 VOID\r
8042 SetGNUMode()\r
8043 {\r
8044   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8045 }\r
8046 \r
8047 VOID\r
8048 SetNCPMode()\r
8049 {\r
8050   HMENU hmenu = GetMenu(hwndMain);\r
8051   SetMenuEnables(hmenu, ncpEnables);\r
8052     DrawMenuBar(hwndMain);\r
8053 }\r
8054 \r
8055 VOID\r
8056 SetCmailMode()\r
8057 {\r
8058   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8059 }\r
8060 \r
8061 VOID \r
8062 SetTrainingModeOn()\r
8063 {\r
8064   int i;\r
8065   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8066   for (i = 0; i < N_BUTTONS; i++) {\r
8067     if (buttonDesc[i].hwnd != NULL)\r
8068       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8069   }\r
8070   CommentPopDown();\r
8071 }\r
8072 \r
8073 VOID SetTrainingModeOff()\r
8074 {\r
8075   int i;\r
8076   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8077   for (i = 0; i < N_BUTTONS; i++) {\r
8078     if (buttonDesc[i].hwnd != NULL)\r
8079       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8080   }\r
8081 }\r
8082 \r
8083 \r
8084 VOID\r
8085 SetUserThinkingEnables()\r
8086 {\r
8087   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8088 }\r
8089 \r
8090 VOID\r
8091 SetMachineThinkingEnables()\r
8092 {\r
8093   HMENU hMenu = GetMenu(hwndMain);\r
8094   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8095 \r
8096   SetMenuEnables(hMenu, machineThinkingEnables);\r
8097 \r
8098   if (gameMode == MachinePlaysBlack) {\r
8099     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8100   } else if (gameMode == MachinePlaysWhite) {\r
8101     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8102   } else if (gameMode == TwoMachinesPlay) {\r
8103     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8104   }\r
8105 }\r
8106 \r
8107 \r
8108 VOID\r
8109 DisplayTitle(char *str)\r
8110 {\r
8111   char title[MSG_SIZ], *host;\r
8112   if (str[0] != NULLCHAR) {\r
8113     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8114   } else if (appData.icsActive) {\r
8115     if (appData.icsCommPort[0] != NULLCHAR)\r
8116       host = "ICS";\r
8117     else \r
8118       host = appData.icsHost;\r
8119       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8120   } else if (appData.noChessProgram) {\r
8121     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8122   } else {\r
8123     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8124     strcat(title, ": ");\r
8125     strcat(title, first.tidy);\r
8126   }\r
8127   SetWindowText(hwndMain, title);\r
8128 }\r
8129 \r
8130 \r
8131 VOID\r
8132 DisplayMessage(char *str1, char *str2)\r
8133 {\r
8134   HDC hdc;\r
8135   HFONT oldFont;\r
8136   int remain = MESSAGE_TEXT_MAX - 1;\r
8137   int len;\r
8138 \r
8139   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8140   messageText[0] = NULLCHAR;\r
8141   if (*str1) {\r
8142     len = strlen(str1);\r
8143     if (len > remain) len = remain;\r
8144     strncpy(messageText, str1, len);\r
8145     messageText[len] = NULLCHAR;\r
8146     remain -= len;\r
8147   }\r
8148   if (*str2 && remain >= 2) {\r
8149     if (*str1) {\r
8150       strcat(messageText, "  ");\r
8151       remain -= 2;\r
8152     }\r
8153     len = strlen(str2);\r
8154     if (len > remain) len = remain;\r
8155     strncat(messageText, str2, len);\r
8156   }\r
8157   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
8158   safeStrCpy(lastMsg, messageText, MSG_SIZ);
8159 \r
8160   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8161 \r
8162   SAYMACHINEMOVE();\r
8163 \r
8164   hdc = GetDC(hwndMain);\r
8165   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8166   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8167              &messageRect, messageText, strlen(messageText), NULL);\r
8168   (void) SelectObject(hdc, oldFont);\r
8169   (void) ReleaseDC(hwndMain, hdc);\r
8170 }\r
8171 \r
8172 VOID\r
8173 DisplayError(char *str, int error)\r
8174 {\r
8175   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8176   int len;\r
8177 \r
8178   if (error == 0) {\r
8179     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8180   } else {\r
8181     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8182                         NULL, error, LANG_NEUTRAL,\r
8183                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8184     if (len > 0) {\r
8185       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8186     } else {\r
8187       ErrorMap *em = errmap;\r
8188       while (em->err != 0 && em->err != error) em++;\r
8189       if (em->err != 0) {\r
8190         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8191       } else {\r
8192         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8193       }\r
8194     }\r
8195   }\r
8196   \r
8197   ErrorPopUp(_("Error"), buf);\r
8198 }\r
8199 \r
8200 \r
8201 VOID\r
8202 DisplayMoveError(char *str)\r
8203 {\r
8204   fromX = fromY = -1;\r
8205   ClearHighlights();\r
8206   DrawPosition(FALSE, NULL);\r
8207   if (appData.popupMoveErrors) {\r
8208     ErrorPopUp(_("Error"), str);\r
8209   } else {\r
8210     DisplayMessage(str, "");\r
8211     moveErrorMessageUp = TRUE;\r
8212   }\r
8213 }\r
8214 \r
8215 VOID\r
8216 DisplayFatalError(char *str, int error, int exitStatus)\r
8217 {\r
8218   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8219   int len;\r
8220   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8221 \r
8222   if (error != 0) {\r
8223     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8224                         NULL, error, LANG_NEUTRAL,\r
8225                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8226     if (len > 0) {\r
8227       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8228     } else {\r
8229       ErrorMap *em = errmap;\r
8230       while (em->err != 0 && em->err != error) em++;\r
8231       if (em->err != 0) {\r
8232         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8233       } else {\r
8234         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8235       }\r
8236     }\r
8237     str = buf;\r
8238   }\r
8239   if (appData.debugMode) {\r
8240     fprintf(debugFP, "%s: %s\n", label, str);\r
8241   }\r
8242   if (appData.popupExitMessage) {\r
8243     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8244                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8245   }\r
8246   ExitEvent(exitStatus);\r
8247 }\r
8248 \r
8249 \r
8250 VOID\r
8251 DisplayInformation(char *str)\r
8252 {\r
8253   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8254 }\r
8255 \r
8256 \r
8257 VOID\r
8258 DisplayNote(char *str)\r
8259 {\r
8260   ErrorPopUp(_("Note"), str);\r
8261 }\r
8262 \r
8263 \r
8264 typedef struct {\r
8265   char *title, *question, *replyPrefix;\r
8266   ProcRef pr;\r
8267 } QuestionParams;\r
8268 \r
8269 LRESULT CALLBACK\r
8270 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8271 {\r
8272   static QuestionParams *qp;\r
8273   char reply[MSG_SIZ];\r
8274   int len, err;\r
8275 \r
8276   switch (message) {\r
8277   case WM_INITDIALOG:\r
8278     qp = (QuestionParams *) lParam;\r
8279     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8280     Translate(hDlg, DLG_Question);\r
8281     SetWindowText(hDlg, qp->title);\r
8282     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8283     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8284     return FALSE;\r
8285 \r
8286   case WM_COMMAND:\r
8287     switch (LOWORD(wParam)) {\r
8288     case IDOK:\r
8289       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8290       if (*reply) strcat(reply, " ");\r
8291       len = strlen(reply);\r
8292       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8293       strcat(reply, "\n");\r
8294       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8295       EndDialog(hDlg, TRUE);\r
8296       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8297       return TRUE;\r
8298     case IDCANCEL:\r
8299       EndDialog(hDlg, FALSE);\r
8300       return TRUE;\r
8301     default:\r
8302       break;\r
8303     }\r
8304     break;\r
8305   }\r
8306   return FALSE;\r
8307 }\r
8308 \r
8309 VOID\r
8310 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8311 {\r
8312     QuestionParams qp;\r
8313     FARPROC lpProc;\r
8314     \r
8315     qp.title = title;\r
8316     qp.question = question;\r
8317     qp.replyPrefix = replyPrefix;\r
8318     qp.pr = pr;\r
8319     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8320     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8321       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8322     FreeProcInstance(lpProc);\r
8323 }\r
8324 \r
8325 /* [AS] Pick FRC position */\r
8326 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8327 {\r
8328     static int * lpIndexFRC;\r
8329     BOOL index_is_ok;\r
8330     char buf[16];\r
8331 \r
8332     switch( message )\r
8333     {\r
8334     case WM_INITDIALOG:\r
8335         lpIndexFRC = (int *) lParam;\r
8336 \r
8337         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8338         Translate(hDlg, DLG_NewGameFRC);\r
8339 \r
8340         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8341         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8342         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8343         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8344 \r
8345         break;\r
8346 \r
8347     case WM_COMMAND:\r
8348         switch( LOWORD(wParam) ) {\r
8349         case IDOK:\r
8350             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8351             EndDialog( hDlg, 0 );\r
8352             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8353             return TRUE;\r
8354         case IDCANCEL:\r
8355             EndDialog( hDlg, 1 );   \r
8356             return TRUE;\r
8357         case IDC_NFG_Edit:\r
8358             if( HIWORD(wParam) == EN_CHANGE ) {\r
8359                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8360 \r
8361                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8362             }\r
8363             return TRUE;\r
8364         case IDC_NFG_Random:\r
8365           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8366             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8367             return TRUE;\r
8368         }\r
8369 \r
8370         break;\r
8371     }\r
8372 \r
8373     return FALSE;\r
8374 }\r
8375 \r
8376 int NewGameFRC()\r
8377 {\r
8378     int result;\r
8379     int index = appData.defaultFrcPosition;\r
8380     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8381 \r
8382     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8383 \r
8384     if( result == 0 ) {\r
8385         appData.defaultFrcPosition = index;\r
8386     }\r
8387 \r
8388     return result;\r
8389 }\r
8390 \r
8391 /* [AS] Game list options. Refactored by HGM */\r
8392 \r
8393 HWND gameListOptionsDialog;\r
8394 \r
8395 // low-level front-end: clear text edit / list widget\r
8396 void\r
8397 GLT_ClearList()\r
8398 {\r
8399     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8400 }\r
8401 \r
8402 // low-level front-end: clear text edit / list widget\r
8403 void\r
8404 GLT_DeSelectList()\r
8405 {\r
8406     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8407 }\r
8408 \r
8409 // low-level front-end: append line to text edit / list widget\r
8410 void\r
8411 GLT_AddToList( char *name )\r
8412 {\r
8413     if( name != 0 ) {\r
8414             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8415     }\r
8416 }\r
8417 \r
8418 // low-level front-end: get line from text edit / list widget\r
8419 Boolean\r
8420 GLT_GetFromList( int index, char *name )\r
8421 {\r
8422     if( name != 0 ) {\r
8423             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8424                 return TRUE;\r
8425     }\r
8426     return FALSE;\r
8427 }\r
8428 \r
8429 void GLT_MoveSelection( HWND hDlg, int delta )\r
8430 {\r
8431     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8432     int idx2 = idx1 + delta;\r
8433     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8434 \r
8435     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8436         char buf[128];\r
8437 \r
8438         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8439         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8440         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8441         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8442     }\r
8443 }\r
8444 \r
8445 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8446 {\r
8447     switch( message )\r
8448     {\r
8449     case WM_INITDIALOG:\r
8450         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8451         \r
8452         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8453         Translate(hDlg, DLG_GameListOptions);\r
8454 \r
8455         /* Initialize list */\r
8456         GLT_TagsToList( lpUserGLT );\r
8457 \r
8458         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8459 \r
8460         break;\r
8461 \r
8462     case WM_COMMAND:\r
8463         switch( LOWORD(wParam) ) {\r
8464         case IDOK:\r
8465             GLT_ParseList();\r
8466             EndDialog( hDlg, 0 );\r
8467             return TRUE;\r
8468         case IDCANCEL:\r
8469             EndDialog( hDlg, 1 );\r
8470             return TRUE;\r
8471 \r
8472         case IDC_GLT_Default:\r
8473             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8474             return TRUE;\r
8475 \r
8476         case IDC_GLT_Restore:\r
8477             GLT_TagsToList( appData.gameListTags );\r
8478             return TRUE;\r
8479 \r
8480         case IDC_GLT_Up:\r
8481             GLT_MoveSelection( hDlg, -1 );\r
8482             return TRUE;\r
8483 \r
8484         case IDC_GLT_Down:\r
8485             GLT_MoveSelection( hDlg, +1 );\r
8486             return TRUE;\r
8487         }\r
8488 \r
8489         break;\r
8490     }\r
8491 \r
8492     return FALSE;\r
8493 }\r
8494 \r
8495 int GameListOptions()\r
8496 {\r
8497     int result;\r
8498     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8499 \r
8500       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8501 \r
8502     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8503 \r
8504     if( result == 0 ) {\r
8505         /* [AS] Memory leak here! */\r
8506         appData.gameListTags = strdup( lpUserGLT ); \r
8507     }\r
8508 \r
8509     return result;\r
8510 }\r
8511 \r
8512 VOID\r
8513 DisplayIcsInteractionTitle(char *str)\r
8514 {\r
8515   char consoleTitle[MSG_SIZ];\r
8516 \r
8517     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8518   SetWindowText(hwndConsole, consoleTitle);\r
8519 }\r
8520 \r
8521 void\r
8522 DrawPosition(int fullRedraw, Board board)\r
8523 {\r
8524   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8525 }\r
8526 \r
8527 void NotifyFrontendLogin()\r
8528 {\r
8529         if (hwndConsole)\r
8530                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8531 }\r
8532 \r
8533 VOID\r
8534 ResetFrontEnd()\r
8535 {\r
8536   fromX = fromY = -1;\r
8537   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8538     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8539     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8540     dragInfo.lastpos = dragInfo.pos;\r
8541     dragInfo.start.x = dragInfo.start.y = -1;\r
8542     dragInfo.from = dragInfo.start;\r
8543     ReleaseCapture();\r
8544     DrawPosition(TRUE, NULL);\r
8545   }\r
8546   TagsPopDown();\r
8547 }\r
8548 \r
8549 \r
8550 VOID\r
8551 CommentPopUp(char *title, char *str)\r
8552 {\r
8553   HWND hwnd = GetActiveWindow();\r
8554   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8555   SAY(str);\r
8556   SetActiveWindow(hwnd);\r
8557 }\r
8558 \r
8559 VOID\r
8560 CommentPopDown(void)\r
8561 {\r
8562   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8563   if (commentDialog) {\r
8564     ShowWindow(commentDialog, SW_HIDE);\r
8565   }\r
8566   commentUp = FALSE;\r
8567 }\r
8568 \r
8569 VOID\r
8570 EditCommentPopUp(int index, char *title, char *str)\r
8571 {\r
8572   EitherCommentPopUp(index, title, str, TRUE);\r
8573 }\r
8574 \r
8575 \r
8576 VOID\r
8577 RingBell()\r
8578 {\r
8579   MyPlaySound(&sounds[(int)SoundMove]);\r
8580 }\r
8581 \r
8582 VOID PlayIcsWinSound()\r
8583 {\r
8584   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8585 }\r
8586 \r
8587 VOID PlayIcsLossSound()\r
8588 {\r
8589   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8590 }\r
8591 \r
8592 VOID PlayIcsDrawSound()\r
8593 {\r
8594   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8595 }\r
8596 \r
8597 VOID PlayIcsUnfinishedSound()\r
8598 {\r
8599   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8600 }\r
8601 \r
8602 VOID\r
8603 PlayAlarmSound()\r
8604 {\r
8605   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8606 }\r
8607 \r
8608 VOID\r
8609 PlayTellSound()\r
8610 {\r
8611   MyPlaySound(&textAttribs[ColorTell].sound);\r
8612 }\r
8613 \r
8614 \r
8615 VOID\r
8616 EchoOn()\r
8617 {\r
8618   HWND hInput;\r
8619   consoleEcho = TRUE;\r
8620   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8621   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8622   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8623 }\r
8624 \r
8625 \r
8626 VOID\r
8627 EchoOff()\r
8628 {\r
8629   CHARFORMAT cf;\r
8630   HWND hInput;\r
8631   consoleEcho = FALSE;\r
8632   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8633   /* This works OK: set text and background both to the same color */\r
8634   cf = consoleCF;\r
8635   cf.crTextColor = COLOR_ECHOOFF;\r
8636   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8637   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8638 }\r
8639 \r
8640 /* No Raw()...? */\r
8641 \r
8642 void Colorize(ColorClass cc, int continuation)\r
8643 {\r
8644   currentColorClass = cc;\r
8645   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8646   consoleCF.crTextColor = textAttribs[cc].color;\r
8647   consoleCF.dwEffects = textAttribs[cc].effects;\r
8648   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8649 }\r
8650 \r
8651 char *\r
8652 UserName()\r
8653 {\r
8654   static char buf[MSG_SIZ];\r
8655   DWORD bufsiz = MSG_SIZ;\r
8656 \r
8657   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8658         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8659   }\r
8660   if (!GetUserName(buf, &bufsiz)) {\r
8661     /*DisplayError("Error getting user name", GetLastError());*/\r
8662     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8663   }\r
8664   return buf;\r
8665 }\r
8666 \r
8667 char *\r
8668 HostName()\r
8669 {\r
8670   static char buf[MSG_SIZ];\r
8671   DWORD bufsiz = MSG_SIZ;\r
8672 \r
8673   if (!GetComputerName(buf, &bufsiz)) {\r
8674     /*DisplayError("Error getting host name", GetLastError());*/\r
8675     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8676   }\r
8677   return buf;\r
8678 }\r
8679 \r
8680 \r
8681 int\r
8682 ClockTimerRunning()\r
8683 {\r
8684   return clockTimerEvent != 0;\r
8685 }\r
8686 \r
8687 int\r
8688 StopClockTimer()\r
8689 {\r
8690   if (clockTimerEvent == 0) return FALSE;\r
8691   KillTimer(hwndMain, clockTimerEvent);\r
8692   clockTimerEvent = 0;\r
8693   return TRUE;\r
8694 }\r
8695 \r
8696 void\r
8697 StartClockTimer(long millisec)\r
8698 {\r
8699   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8700                              (UINT) millisec, NULL);\r
8701 }\r
8702 \r
8703 void\r
8704 DisplayWhiteClock(long timeRemaining, int highlight)\r
8705 {\r
8706   HDC hdc;\r
8707   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8708 \r
8709   if(appData.noGUI) return;\r
8710   hdc = GetDC(hwndMain);\r
8711   if (!IsIconic(hwndMain)) {\r
8712     DisplayAClock(hdc, timeRemaining, highlight, \r
8713                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8714   }\r
8715   if (highlight && iconCurrent == iconBlack) {\r
8716     iconCurrent = iconWhite;\r
8717     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8718     if (IsIconic(hwndMain)) {\r
8719       DrawIcon(hdc, 2, 2, iconCurrent);\r
8720     }\r
8721   }\r
8722   (void) ReleaseDC(hwndMain, hdc);\r
8723   if (hwndConsole)\r
8724     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8725 }\r
8726 \r
8727 void\r
8728 DisplayBlackClock(long timeRemaining, int highlight)\r
8729 {\r
8730   HDC hdc;\r
8731   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8732 \r
8733   if(appData.noGUI) return;\r
8734   hdc = GetDC(hwndMain);\r
8735   if (!IsIconic(hwndMain)) {\r
8736     DisplayAClock(hdc, timeRemaining, highlight, \r
8737                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8738   }\r
8739   if (highlight && iconCurrent == iconWhite) {\r
8740     iconCurrent = iconBlack;\r
8741     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8742     if (IsIconic(hwndMain)) {\r
8743       DrawIcon(hdc, 2, 2, iconCurrent);\r
8744     }\r
8745   }\r
8746   (void) ReleaseDC(hwndMain, hdc);\r
8747   if (hwndConsole)\r
8748     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8749 }\r
8750 \r
8751 \r
8752 int\r
8753 LoadGameTimerRunning()\r
8754 {\r
8755   return loadGameTimerEvent != 0;\r
8756 }\r
8757 \r
8758 int\r
8759 StopLoadGameTimer()\r
8760 {\r
8761   if (loadGameTimerEvent == 0) return FALSE;\r
8762   KillTimer(hwndMain, loadGameTimerEvent);\r
8763   loadGameTimerEvent = 0;\r
8764   return TRUE;\r
8765 }\r
8766 \r
8767 void\r
8768 StartLoadGameTimer(long millisec)\r
8769 {\r
8770   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8771                                 (UINT) millisec, NULL);\r
8772 }\r
8773 \r
8774 void\r
8775 AutoSaveGame()\r
8776 {\r
8777   char *defName;\r
8778   FILE *f;\r
8779   char fileTitle[MSG_SIZ];\r
8780 \r
8781   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8782   f = OpenFileDialog(hwndMain, "a", defName,\r
8783                      appData.oldSaveStyle ? "gam" : "pgn",\r
8784                      GAME_FILT, \r
8785                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8786   if (f != NULL) {\r
8787     SaveGame(f, 0, "");\r
8788     fclose(f);\r
8789   }\r
8790 }\r
8791 \r
8792 \r
8793 void\r
8794 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8795 {\r
8796   if (delayedTimerEvent != 0) {\r
8797     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8798       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8799     }\r
8800     KillTimer(hwndMain, delayedTimerEvent);\r
8801     delayedTimerEvent = 0;\r
8802     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8803     delayedTimerCallback();\r
8804   }\r
8805   delayedTimerCallback = cb;\r
8806   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8807                                 (UINT) millisec, NULL);\r
8808 }\r
8809 \r
8810 DelayedEventCallback\r
8811 GetDelayedEvent()\r
8812 {\r
8813   if (delayedTimerEvent) {\r
8814     return delayedTimerCallback;\r
8815   } else {\r
8816     return NULL;\r
8817   }\r
8818 }\r
8819 \r
8820 void\r
8821 CancelDelayedEvent()\r
8822 {\r
8823   if (delayedTimerEvent) {\r
8824     KillTimer(hwndMain, delayedTimerEvent);\r
8825     delayedTimerEvent = 0;\r
8826   }\r
8827 }\r
8828 \r
8829 DWORD GetWin32Priority(int nice)\r
8830 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8831 /*\r
8832 REALTIME_PRIORITY_CLASS     0x00000100\r
8833 HIGH_PRIORITY_CLASS         0x00000080\r
8834 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8835 NORMAL_PRIORITY_CLASS       0x00000020\r
8836 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8837 IDLE_PRIORITY_CLASS         0x00000040\r
8838 */\r
8839         if (nice < -15) return 0x00000080;\r
8840         if (nice < 0)   return 0x00008000;\r
8841         if (nice == 0)  return 0x00000020;\r
8842         if (nice < 15)  return 0x00004000;\r
8843         return 0x00000040;\r
8844 }\r
8845 \r
8846 /* Start a child process running the given program.\r
8847    The process's standard output can be read from "from", and its\r
8848    standard input can be written to "to".\r
8849    Exit with fatal error if anything goes wrong.\r
8850    Returns an opaque pointer that can be used to destroy the process\r
8851    later.\r
8852 */\r
8853 int\r
8854 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8855 {\r
8856 #define BUFSIZE 4096\r
8857 \r
8858   HANDLE hChildStdinRd, hChildStdinWr,\r
8859     hChildStdoutRd, hChildStdoutWr;\r
8860   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8861   SECURITY_ATTRIBUTES saAttr;\r
8862   BOOL fSuccess;\r
8863   PROCESS_INFORMATION piProcInfo;\r
8864   STARTUPINFO siStartInfo;\r
8865   ChildProc *cp;\r
8866   char buf[MSG_SIZ];\r
8867   DWORD err;\r
8868 \r
8869   if (appData.debugMode) {\r
8870     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8871   }\r
8872 \r
8873   *pr = NoProc;\r
8874 \r
8875   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8876   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8877   saAttr.bInheritHandle = TRUE;\r
8878   saAttr.lpSecurityDescriptor = NULL;\r
8879 \r
8880   /*\r
8881    * The steps for redirecting child's STDOUT:\r
8882    *     1. Create anonymous pipe to be STDOUT for child.\r
8883    *     2. Create a noninheritable duplicate of read handle,\r
8884    *         and close the inheritable read handle.\r
8885    */\r
8886 \r
8887   /* Create a pipe for the child's STDOUT. */\r
8888   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8889     return GetLastError();\r
8890   }\r
8891 \r
8892   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8893   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8894                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8895                              FALSE,     /* not inherited */\r
8896                              DUPLICATE_SAME_ACCESS);\r
8897   if (! fSuccess) {\r
8898     return GetLastError();\r
8899   }\r
8900   CloseHandle(hChildStdoutRd);\r
8901 \r
8902   /*\r
8903    * The steps for redirecting child's STDIN:\r
8904    *     1. Create anonymous pipe to be STDIN for child.\r
8905    *     2. Create a noninheritable duplicate of write handle,\r
8906    *         and close the inheritable write handle.\r
8907    */\r
8908 \r
8909   /* Create a pipe for the child's STDIN. */\r
8910   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8911     return GetLastError();\r
8912   }\r
8913 \r
8914   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8915   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8916                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8917                              FALSE,     /* not inherited */\r
8918                              DUPLICATE_SAME_ACCESS);\r
8919   if (! fSuccess) {\r
8920     return GetLastError();\r
8921   }\r
8922   CloseHandle(hChildStdinWr);\r
8923 \r
8924   /* Arrange to (1) look in dir for the child .exe file, and\r
8925    * (2) have dir be the child's working directory.  Interpret\r
8926    * dir relative to the directory WinBoard loaded from. */\r
8927   GetCurrentDirectory(MSG_SIZ, buf);\r
8928   SetCurrentDirectory(installDir);\r
8929   SetCurrentDirectory(dir);\r
8930 \r
8931   /* Now create the child process. */\r
8932 \r
8933   siStartInfo.cb = sizeof(STARTUPINFO);\r
8934   siStartInfo.lpReserved = NULL;\r
8935   siStartInfo.lpDesktop = NULL;\r
8936   siStartInfo.lpTitle = NULL;\r
8937   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8938   siStartInfo.cbReserved2 = 0;\r
8939   siStartInfo.lpReserved2 = NULL;\r
8940   siStartInfo.hStdInput = hChildStdinRd;\r
8941   siStartInfo.hStdOutput = hChildStdoutWr;\r
8942   siStartInfo.hStdError = hChildStdoutWr;\r
8943 \r
8944   fSuccess = CreateProcess(NULL,\r
8945                            cmdLine,        /* command line */\r
8946                            NULL,           /* process security attributes */\r
8947                            NULL,           /* primary thread security attrs */\r
8948                            TRUE,           /* handles are inherited */\r
8949                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8950                            NULL,           /* use parent's environment */\r
8951                            NULL,\r
8952                            &siStartInfo, /* STARTUPINFO pointer */\r
8953                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
8954 \r
8955   err = GetLastError();\r
8956   SetCurrentDirectory(buf); /* return to prev directory */\r
8957   if (! fSuccess) {\r
8958     return err;\r
8959   }\r
8960 \r
8961   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
8962     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
8963     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
8964   }\r
8965 \r
8966   /* Close the handles we don't need in the parent */\r
8967   CloseHandle(piProcInfo.hThread);\r
8968   CloseHandle(hChildStdinRd);\r
8969   CloseHandle(hChildStdoutWr);\r
8970 \r
8971   /* Prepare return value */\r
8972   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8973   cp->kind = CPReal;\r
8974   cp->hProcess = piProcInfo.hProcess;\r
8975   cp->pid = piProcInfo.dwProcessId;\r
8976   cp->hFrom = hChildStdoutRdDup;\r
8977   cp->hTo = hChildStdinWrDup;\r
8978 \r
8979   *pr = (void *) cp;\r
8980 \r
8981   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
8982      2000 where engines sometimes don't see the initial command(s)\r
8983      from WinBoard and hang.  I don't understand how that can happen,\r
8984      but the Sleep is harmless, so I've put it in.  Others have also\r
8985      reported what may be the same problem, so hopefully this will fix\r
8986      it for them too.  */\r
8987   Sleep(500);\r
8988 \r
8989   return NO_ERROR;\r
8990 }\r
8991 \r
8992 \r
8993 void\r
8994 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
8995 {\r
8996   ChildProc *cp; int result;\r
8997 \r
8998   cp = (ChildProc *) pr;\r
8999   if (cp == NULL) return;\r
9000 \r
9001   switch (cp->kind) {\r
9002   case CPReal:\r
9003     /* TerminateProcess is considered harmful, so... */\r
9004     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9005     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9006     /* The following doesn't work because the chess program\r
9007        doesn't "have the same console" as WinBoard.  Maybe\r
9008        we could arrange for this even though neither WinBoard\r
9009        nor the chess program uses a console for stdio? */\r
9010     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9011 \r
9012     /* [AS] Special termination modes for misbehaving programs... */\r
9013     if( signal == 9 ) { \r
9014         result = TerminateProcess( cp->hProcess, 0 );\r
9015 \r
9016         if ( appData.debugMode) {\r
9017             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9018         }\r
9019     }\r
9020     else if( signal == 10 ) {\r
9021         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9022 \r
9023         if( dw != WAIT_OBJECT_0 ) {\r
9024             result = TerminateProcess( cp->hProcess, 0 );\r
9025 \r
9026             if ( appData.debugMode) {\r
9027                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9028             }\r
9029 \r
9030         }\r
9031     }\r
9032 \r
9033     CloseHandle(cp->hProcess);\r
9034     break;\r
9035 \r
9036   case CPComm:\r
9037     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9038     break;\r
9039 \r
9040   case CPSock:\r
9041     closesocket(cp->sock);\r
9042     WSACleanup();\r
9043     break;\r
9044 \r
9045   case CPRcmd:\r
9046     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9047     closesocket(cp->sock);\r
9048     closesocket(cp->sock2);\r
9049     WSACleanup();\r
9050     break;\r
9051   }\r
9052   free(cp);\r
9053 }\r
9054 \r
9055 void\r
9056 InterruptChildProcess(ProcRef pr)\r
9057 {\r
9058   ChildProc *cp;\r
9059 \r
9060   cp = (ChildProc *) pr;\r
9061   if (cp == NULL) return;\r
9062   switch (cp->kind) {\r
9063   case CPReal:\r
9064     /* The following doesn't work because the chess program\r
9065        doesn't "have the same console" as WinBoard.  Maybe\r
9066        we could arrange for this even though neither WinBoard\r
9067        nor the chess program uses a console for stdio */\r
9068     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9069     break;\r
9070 \r
9071   case CPComm:\r
9072   case CPSock:\r
9073     /* Can't interrupt */\r
9074     break;\r
9075 \r
9076   case CPRcmd:\r
9077     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9078     break;\r
9079   }\r
9080 }\r
9081 \r
9082 \r
9083 int\r
9084 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9085 {\r
9086   char cmdLine[MSG_SIZ];\r
9087 \r
9088   if (port[0] == NULLCHAR) {\r
9089     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9090   } else {\r
9091     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9092   }\r
9093   return StartChildProcess(cmdLine, "", pr);\r
9094 }\r
9095 \r
9096 \r
9097 /* Code to open TCP sockets */\r
9098 \r
9099 int\r
9100 OpenTCP(char *host, char *port, ProcRef *pr)\r
9101 {\r
9102   ChildProc *cp;\r
9103   int err;\r
9104   SOCKET s;\r
9105   struct sockaddr_in sa, mysa;\r
9106   struct hostent FAR *hp;\r
9107   unsigned short uport;\r
9108   WORD wVersionRequested;\r
9109   WSADATA wsaData;\r
9110 \r
9111   /* Initialize socket DLL */\r
9112   wVersionRequested = MAKEWORD(1, 1);\r
9113   err = WSAStartup(wVersionRequested, &wsaData);\r
9114   if (err != 0) return err;\r
9115 \r
9116   /* Make socket */\r
9117   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9118     err = WSAGetLastError();\r
9119     WSACleanup();\r
9120     return err;\r
9121   }\r
9122 \r
9123   /* Bind local address using (mostly) don't-care values.\r
9124    */\r
9125   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9126   mysa.sin_family = AF_INET;\r
9127   mysa.sin_addr.s_addr = INADDR_ANY;\r
9128   uport = (unsigned short) 0;\r
9129   mysa.sin_port = htons(uport);\r
9130   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9131       == SOCKET_ERROR) {\r
9132     err = WSAGetLastError();\r
9133     WSACleanup();\r
9134     return err;\r
9135   }\r
9136 \r
9137   /* Resolve remote host name */\r
9138   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9139   if (!(hp = gethostbyname(host))) {\r
9140     unsigned int b0, b1, b2, b3;\r
9141 \r
9142     err = WSAGetLastError();\r
9143 \r
9144     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9145       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9146       hp->h_addrtype = AF_INET;\r
9147       hp->h_length = 4;\r
9148       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9149       hp->h_addr_list[0] = (char *) malloc(4);\r
9150       hp->h_addr_list[0][0] = (char) b0;\r
9151       hp->h_addr_list[0][1] = (char) b1;\r
9152       hp->h_addr_list[0][2] = (char) b2;\r
9153       hp->h_addr_list[0][3] = (char) b3;\r
9154     } else {\r
9155       WSACleanup();\r
9156       return err;\r
9157     }\r
9158   }\r
9159   sa.sin_family = hp->h_addrtype;\r
9160   uport = (unsigned short) atoi(port);\r
9161   sa.sin_port = htons(uport);\r
9162   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9163 \r
9164   /* Make connection */\r
9165   if (connect(s, (struct sockaddr *) &sa,\r
9166               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9167     err = WSAGetLastError();\r
9168     WSACleanup();\r
9169     return err;\r
9170   }\r
9171 \r
9172   /* Prepare return value */\r
9173   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9174   cp->kind = CPSock;\r
9175   cp->sock = s;\r
9176   *pr = (ProcRef *) cp;\r
9177 \r
9178   return NO_ERROR;\r
9179 }\r
9180 \r
9181 int\r
9182 OpenCommPort(char *name, ProcRef *pr)\r
9183 {\r
9184   HANDLE h;\r
9185   COMMTIMEOUTS ct;\r
9186   ChildProc *cp;\r
9187   char fullname[MSG_SIZ];\r
9188 \r
9189   if (*name != '\\')\r
9190     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9191   else\r
9192     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9193 \r
9194   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9195                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9196   if (h == (HANDLE) -1) {\r
9197     return GetLastError();\r
9198   }\r
9199   hCommPort = h;\r
9200 \r
9201   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9202 \r
9203   /* Accumulate characters until a 100ms pause, then parse */\r
9204   ct.ReadIntervalTimeout = 100;\r
9205   ct.ReadTotalTimeoutMultiplier = 0;\r
9206   ct.ReadTotalTimeoutConstant = 0;\r
9207   ct.WriteTotalTimeoutMultiplier = 0;\r
9208   ct.WriteTotalTimeoutConstant = 0;\r
9209   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9210 \r
9211   /* Prepare return value */\r
9212   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9213   cp->kind = CPComm;\r
9214   cp->hFrom = h;\r
9215   cp->hTo = h;\r
9216   *pr = (ProcRef *) cp;\r
9217 \r
9218   return NO_ERROR;\r
9219 }\r
9220 \r
9221 int\r
9222 OpenLoopback(ProcRef *pr)\r
9223 {\r
9224   DisplayFatalError(_("Not implemented"), 0, 1);\r
9225   return NO_ERROR;\r
9226 }\r
9227 \r
9228 \r
9229 int\r
9230 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9231 {\r
9232   ChildProc *cp;\r
9233   int err;\r
9234   SOCKET s, s2, s3;\r
9235   struct sockaddr_in sa, mysa;\r
9236   struct hostent FAR *hp;\r
9237   unsigned short uport;\r
9238   WORD wVersionRequested;\r
9239   WSADATA wsaData;\r
9240   int fromPort;\r
9241   char stderrPortStr[MSG_SIZ];\r
9242 \r
9243   /* Initialize socket DLL */\r
9244   wVersionRequested = MAKEWORD(1, 1);\r
9245   err = WSAStartup(wVersionRequested, &wsaData);\r
9246   if (err != 0) return err;\r
9247 \r
9248   /* Resolve remote host name */\r
9249   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9250   if (!(hp = gethostbyname(host))) {\r
9251     unsigned int b0, b1, b2, b3;\r
9252 \r
9253     err = WSAGetLastError();\r
9254 \r
9255     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9256       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9257       hp->h_addrtype = AF_INET;\r
9258       hp->h_length = 4;\r
9259       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9260       hp->h_addr_list[0] = (char *) malloc(4);\r
9261       hp->h_addr_list[0][0] = (char) b0;\r
9262       hp->h_addr_list[0][1] = (char) b1;\r
9263       hp->h_addr_list[0][2] = (char) b2;\r
9264       hp->h_addr_list[0][3] = (char) b3;\r
9265     } else {\r
9266       WSACleanup();\r
9267       return err;\r
9268     }\r
9269   }\r
9270   sa.sin_family = hp->h_addrtype;\r
9271   uport = (unsigned short) 514;\r
9272   sa.sin_port = htons(uport);\r
9273   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9274 \r
9275   /* Bind local socket to unused "privileged" port address\r
9276    */\r
9277   s = INVALID_SOCKET;\r
9278   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9279   mysa.sin_family = AF_INET;\r
9280   mysa.sin_addr.s_addr = INADDR_ANY;\r
9281   for (fromPort = 1023;; fromPort--) {\r
9282     if (fromPort < 0) {\r
9283       WSACleanup();\r
9284       return WSAEADDRINUSE;\r
9285     }\r
9286     if (s == INVALID_SOCKET) {\r
9287       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9288         err = WSAGetLastError();\r
9289         WSACleanup();\r
9290         return err;\r
9291       }\r
9292     }\r
9293     uport = (unsigned short) fromPort;\r
9294     mysa.sin_port = htons(uport);\r
9295     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9296         == SOCKET_ERROR) {\r
9297       err = WSAGetLastError();\r
9298       if (err == WSAEADDRINUSE) continue;\r
9299       WSACleanup();\r
9300       return err;\r
9301     }\r
9302     if (connect(s, (struct sockaddr *) &sa,\r
9303       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9304       err = WSAGetLastError();\r
9305       if (err == WSAEADDRINUSE) {\r
9306         closesocket(s);\r
9307         s = -1;\r
9308         continue;\r
9309       }\r
9310       WSACleanup();\r
9311       return err;\r
9312     }\r
9313     break;\r
9314   }\r
9315 \r
9316   /* Bind stderr local socket to unused "privileged" port address\r
9317    */\r
9318   s2 = INVALID_SOCKET;\r
9319   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9320   mysa.sin_family = AF_INET;\r
9321   mysa.sin_addr.s_addr = INADDR_ANY;\r
9322   for (fromPort = 1023;; fromPort--) {\r
9323     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9324     if (fromPort < 0) {\r
9325       (void) closesocket(s);\r
9326       WSACleanup();\r
9327       return WSAEADDRINUSE;\r
9328     }\r
9329     if (s2 == INVALID_SOCKET) {\r
9330       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9331         err = WSAGetLastError();\r
9332         closesocket(s);\r
9333         WSACleanup();\r
9334         return err;\r
9335       }\r
9336     }\r
9337     uport = (unsigned short) fromPort;\r
9338     mysa.sin_port = htons(uport);\r
9339     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9340         == SOCKET_ERROR) {\r
9341       err = WSAGetLastError();\r
9342       if (err == WSAEADDRINUSE) continue;\r
9343       (void) closesocket(s);\r
9344       WSACleanup();\r
9345       return err;\r
9346     }\r
9347     if (listen(s2, 1) == SOCKET_ERROR) {\r
9348       err = WSAGetLastError();\r
9349       if (err == WSAEADDRINUSE) {\r
9350         closesocket(s2);\r
9351         s2 = INVALID_SOCKET;\r
9352         continue;\r
9353       }\r
9354       (void) closesocket(s);\r
9355       (void) closesocket(s2);\r
9356       WSACleanup();\r
9357       return err;\r
9358     }\r
9359     break;\r
9360   }\r
9361   prevStderrPort = fromPort; // remember port used\r
9362   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9363 \r
9364   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9365     err = WSAGetLastError();\r
9366     (void) closesocket(s);\r
9367     (void) closesocket(s2);\r
9368     WSACleanup();\r
9369     return err;\r
9370   }\r
9371 \r
9372   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9373     err = WSAGetLastError();\r
9374     (void) closesocket(s);\r
9375     (void) closesocket(s2);\r
9376     WSACleanup();\r
9377     return err;\r
9378   }\r
9379   if (*user == NULLCHAR) user = UserName();\r
9380   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9381     err = WSAGetLastError();\r
9382     (void) closesocket(s);\r
9383     (void) closesocket(s2);\r
9384     WSACleanup();\r
9385     return err;\r
9386   }\r
9387   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9388     err = WSAGetLastError();\r
9389     (void) closesocket(s);\r
9390     (void) closesocket(s2);\r
9391     WSACleanup();\r
9392     return err;\r
9393   }\r
9394 \r
9395   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9396     err = WSAGetLastError();\r
9397     (void) closesocket(s);\r
9398     (void) closesocket(s2);\r
9399     WSACleanup();\r
9400     return err;\r
9401   }\r
9402   (void) closesocket(s2);  /* Stop listening */\r
9403 \r
9404   /* Prepare return value */\r
9405   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9406   cp->kind = CPRcmd;\r
9407   cp->sock = s;\r
9408   cp->sock2 = s3;\r
9409   *pr = (ProcRef *) cp;\r
9410 \r
9411   return NO_ERROR;\r
9412 }\r
9413 \r
9414 \r
9415 InputSourceRef\r
9416 AddInputSource(ProcRef pr, int lineByLine,\r
9417                InputCallback func, VOIDSTAR closure)\r
9418 {\r
9419   InputSource *is, *is2 = NULL;\r
9420   ChildProc *cp = (ChildProc *) pr;\r
9421 \r
9422   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9423   is->lineByLine = lineByLine;\r
9424   is->func = func;\r
9425   is->closure = closure;\r
9426   is->second = NULL;\r
9427   is->next = is->buf;\r
9428   if (pr == NoProc) {\r
9429     is->kind = CPReal;\r
9430     consoleInputSource = is;\r
9431   } else {\r
9432     is->kind = cp->kind;\r
9433     /* \r
9434         [AS] Try to avoid a race condition if the thread is given control too early:\r
9435         we create all threads suspended so that the is->hThread variable can be\r
9436         safely assigned, then let the threads start with ResumeThread.\r
9437     */\r
9438     switch (cp->kind) {\r
9439     case CPReal:\r
9440       is->hFile = cp->hFrom;\r
9441       cp->hFrom = NULL; /* now owned by InputThread */\r
9442       is->hThread =\r
9443         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9444                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9445       break;\r
9446 \r
9447     case CPComm:\r
9448       is->hFile = cp->hFrom;\r
9449       cp->hFrom = NULL; /* now owned by InputThread */\r
9450       is->hThread =\r
9451         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9452                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9453       break;\r
9454 \r
9455     case CPSock:\r
9456       is->sock = cp->sock;\r
9457       is->hThread =\r
9458         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9459                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9460       break;\r
9461 \r
9462     case CPRcmd:\r
9463       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9464       *is2 = *is;\r
9465       is->sock = cp->sock;\r
9466       is->second = is2;\r
9467       is2->sock = cp->sock2;\r
9468       is2->second = is2;\r
9469       is->hThread =\r
9470         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9471                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9472       is2->hThread =\r
9473         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9474                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9475       break;\r
9476     }\r
9477 \r
9478     if( is->hThread != NULL ) {\r
9479         ResumeThread( is->hThread );\r
9480     }\r
9481 \r
9482     if( is2 != NULL && is2->hThread != NULL ) {\r
9483         ResumeThread( is2->hThread );\r
9484     }\r
9485   }\r
9486 \r
9487   return (InputSourceRef) is;\r
9488 }\r
9489 \r
9490 void\r
9491 RemoveInputSource(InputSourceRef isr)\r
9492 {\r
9493   InputSource *is;\r
9494 \r
9495   is = (InputSource *) isr;\r
9496   is->hThread = NULL;  /* tell thread to stop */\r
9497   CloseHandle(is->hThread);\r
9498   if (is->second != NULL) {\r
9499     is->second->hThread = NULL;\r
9500     CloseHandle(is->second->hThread);\r
9501   }\r
9502 }\r
9503 \r
9504 int no_wrap(char *message, int count)\r
9505 {\r
9506     ConsoleOutput(message, count, FALSE);\r
9507     return count;\r
9508 }\r
9509 \r
9510 int\r
9511 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9512 {\r
9513   DWORD dOutCount;\r
9514   int outCount = SOCKET_ERROR;\r
9515   ChildProc *cp = (ChildProc *) pr;\r
9516   static OVERLAPPED ovl;\r
9517   static int line = 0;\r
9518 \r
9519   if (pr == NoProc)\r
9520   {\r
9521     if (appData.noJoin || !appData.useInternalWrap)\r
9522       return no_wrap(message, count);\r
9523     else\r
9524     {\r
9525       int width = get_term_width();\r
9526       int len = wrap(NULL, message, count, width, &line);\r
9527       char *msg = malloc(len);\r
9528       int dbgchk;\r
9529 \r
9530       if (!msg)\r
9531         return no_wrap(message, count);\r
9532       else\r
9533       {\r
9534         dbgchk = wrap(msg, message, count, width, &line);\r
9535         if (dbgchk != len && appData.debugMode)\r
9536             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9537         ConsoleOutput(msg, len, FALSE);\r
9538         free(msg);\r
9539         return len;\r
9540       }\r
9541     }\r
9542   }\r
9543 \r
9544   if (ovl.hEvent == NULL) {\r
9545     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9546   }\r
9547   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9548 \r
9549   switch (cp->kind) {\r
9550   case CPSock:\r
9551   case CPRcmd:\r
9552     outCount = send(cp->sock, message, count, 0);\r
9553     if (outCount == SOCKET_ERROR) {\r
9554       *outError = WSAGetLastError();\r
9555     } else {\r
9556       *outError = NO_ERROR;\r
9557     }\r
9558     break;\r
9559 \r
9560   case CPReal:\r
9561     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9562                   &dOutCount, NULL)) {\r
9563       *outError = NO_ERROR;\r
9564       outCount = (int) dOutCount;\r
9565     } else {\r
9566       *outError = GetLastError();\r
9567     }\r
9568     break;\r
9569 \r
9570   case CPComm:\r
9571     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9572                             &dOutCount, &ovl);\r
9573     if (*outError == NO_ERROR) {\r
9574       outCount = (int) dOutCount;\r
9575     }\r
9576     break;\r
9577   }\r
9578   return outCount;\r
9579 }\r
9580 \r
9581 int\r
9582 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9583                        long msdelay)\r
9584 {\r
9585   /* Ignore delay, not implemented for WinBoard */\r
9586   return OutputToProcess(pr, message, count, outError);\r
9587 }\r
9588 \r
9589 \r
9590 void\r
9591 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9592                         char *buf, int count, int error)\r
9593 {\r
9594   DisplayFatalError(_("Not implemented"), 0, 1);\r
9595 }\r
9596 \r
9597 /* see wgamelist.c for Game List functions */\r
9598 /* see wedittags.c for Edit Tags functions */\r
9599 \r
9600 \r
9601 VOID\r
9602 ICSInitScript()\r
9603 {\r
9604   FILE *f;\r
9605   char buf[MSG_SIZ];\r
9606   char *dummy;\r
9607 \r
9608   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9609     f = fopen(buf, "r");\r
9610     if (f != NULL) {\r
9611       ProcessICSInitScript(f);\r
9612       fclose(f);\r
9613     }\r
9614   }\r
9615 }\r
9616 \r
9617 \r
9618 VOID\r
9619 StartAnalysisClock()\r
9620 {\r
9621   if (analysisTimerEvent) return;\r
9622   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9623                                         (UINT) 2000, NULL);\r
9624 }\r
9625 \r
9626 VOID\r
9627 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9628 {\r
9629   highlightInfo.sq[0].x = fromX;\r
9630   highlightInfo.sq[0].y = fromY;\r
9631   highlightInfo.sq[1].x = toX;\r
9632   highlightInfo.sq[1].y = toY;\r
9633 }\r
9634 \r
9635 VOID\r
9636 ClearHighlights()\r
9637 {\r
9638   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9639     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9640 }\r
9641 \r
9642 VOID\r
9643 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9644 {\r
9645   premoveHighlightInfo.sq[0].x = fromX;\r
9646   premoveHighlightInfo.sq[0].y = fromY;\r
9647   premoveHighlightInfo.sq[1].x = toX;\r
9648   premoveHighlightInfo.sq[1].y = toY;\r
9649 }\r
9650 \r
9651 VOID\r
9652 ClearPremoveHighlights()\r
9653 {\r
9654   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9655     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9656 }\r
9657 \r
9658 VOID\r
9659 ShutDownFrontEnd()\r
9660 {\r
9661   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9662   DeleteClipboardTempFiles();\r
9663 }\r
9664 \r
9665 void\r
9666 BoardToTop()\r
9667 {\r
9668     if (IsIconic(hwndMain))\r
9669       ShowWindow(hwndMain, SW_RESTORE);\r
9670 \r
9671     SetActiveWindow(hwndMain);\r
9672 }\r
9673 \r
9674 /*\r
9675  * Prototypes for animation support routines\r
9676  */\r
9677 static void ScreenSquare(int column, int row, POINT * pt);\r
9678 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9679      POINT frames[], int * nFrames);\r
9680 \r
9681 \r
9682 #define kFactor 4\r
9683 \r
9684 void\r
9685 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9686 {       // [HGM] atomic: animate blast wave\r
9687         int i;\r
9688 \r
9689         explodeInfo.fromX = fromX;\r
9690         explodeInfo.fromY = fromY;\r
9691         explodeInfo.toX = toX;\r
9692         explodeInfo.toY = toY;\r
9693         for(i=1; i<4*kFactor; i++) {\r
9694             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9695             DrawPosition(FALSE, board);\r
9696             Sleep(appData.animSpeed);\r
9697         }\r
9698         explodeInfo.radius = 0;\r
9699         DrawPosition(TRUE, board);\r
9700 }\r
9701 \r
9702 void\r
9703 AnimateMove(board, fromX, fromY, toX, toY)\r
9704      Board board;\r
9705      int fromX;\r
9706      int fromY;\r
9707      int toX;\r
9708      int toY;\r
9709 {\r
9710   ChessSquare piece;\r
9711   POINT start, finish, mid;\r
9712   POINT frames[kFactor * 2 + 1];\r
9713   int nFrames, n;\r
9714 \r
9715   if (!appData.animate) return;\r
9716   if (doingSizing) return;\r
9717   if (fromY < 0 || fromX < 0) return;\r
9718   piece = board[fromY][fromX];\r
9719   if (piece >= EmptySquare) return;\r
9720 \r
9721   ScreenSquare(fromX, fromY, &start);\r
9722   ScreenSquare(toX, toY, &finish);\r
9723 \r
9724   /* All moves except knight jumps move in straight line */\r
9725   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9726     mid.x = start.x + (finish.x - start.x) / 2;\r
9727     mid.y = start.y + (finish.y - start.y) / 2;\r
9728   } else {\r
9729     /* Knight: make straight movement then diagonal */\r
9730     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9731        mid.x = start.x + (finish.x - start.x) / 2;\r
9732        mid.y = start.y;\r
9733      } else {\r
9734        mid.x = start.x;\r
9735        mid.y = start.y + (finish.y - start.y) / 2;\r
9736      }\r
9737   }\r
9738   \r
9739   /* Don't use as many frames for very short moves */\r
9740   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9741     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9742   else\r
9743     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9744 \r
9745   animInfo.from.x = fromX;\r
9746   animInfo.from.y = fromY;\r
9747   animInfo.to.x = toX;\r
9748   animInfo.to.y = toY;\r
9749   animInfo.lastpos = start;\r
9750   animInfo.piece = piece;\r
9751   for (n = 0; n < nFrames; n++) {\r
9752     animInfo.pos = frames[n];\r
9753     DrawPosition(FALSE, NULL);\r
9754     animInfo.lastpos = animInfo.pos;\r
9755     Sleep(appData.animSpeed);\r
9756   }\r
9757   animInfo.pos = finish;\r
9758   DrawPosition(FALSE, NULL);\r
9759   animInfo.piece = EmptySquare;\r
9760   Explode(board, fromX, fromY, toX, toY);\r
9761 }\r
9762 \r
9763 /*      Convert board position to corner of screen rect and color       */\r
9764 \r
9765 static void\r
9766 ScreenSquare(column, row, pt)\r
9767      int column; int row; POINT * pt;\r
9768 {\r
9769   if (flipView) {\r
9770     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9771     pt->y = lineGap + row * (squareSize + lineGap);\r
9772   } else {\r
9773     pt->x = lineGap + column * (squareSize + lineGap);\r
9774     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9775   }\r
9776 }\r
9777 \r
9778 /*      Generate a series of frame coords from start->mid->finish.\r
9779         The movement rate doubles until the half way point is\r
9780         reached, then halves back down to the final destination,\r
9781         which gives a nice slow in/out effect. The algorithmn\r
9782         may seem to generate too many intermediates for short\r
9783         moves, but remember that the purpose is to attract the\r
9784         viewers attention to the piece about to be moved and\r
9785         then to where it ends up. Too few frames would be less\r
9786         noticeable.                                             */\r
9787 \r
9788 static void\r
9789 Tween(start, mid, finish, factor, frames, nFrames)\r
9790      POINT * start; POINT * mid;\r
9791      POINT * finish; int factor;\r
9792      POINT frames[]; int * nFrames;\r
9793 {\r
9794   int n, fraction = 1, count = 0;\r
9795 \r
9796   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9797   for (n = 0; n < factor; n++)\r
9798     fraction *= 2;\r
9799   for (n = 0; n < factor; n++) {\r
9800     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9801     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9802     count ++;\r
9803     fraction = fraction / 2;\r
9804   }\r
9805   \r
9806   /* Midpoint */\r
9807   frames[count] = *mid;\r
9808   count ++;\r
9809   \r
9810   /* Slow out, stepping 1/2, then 1/4, ... */\r
9811   fraction = 2;\r
9812   for (n = 0; n < factor; n++) {\r
9813     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9814     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9815     count ++;\r
9816     fraction = fraction * 2;\r
9817   }\r
9818   *nFrames = count;\r
9819 }\r
9820 \r
9821 void\r
9822 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9823 {\r
9824     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9825 \r
9826     EvalGraphSet( first, last, current, pvInfoList );\r
9827 \r
9828     MakeEngineOutputTitle();\r
9829 }\r
9830 \r
9831 void\r
9832 SettingsPopUp(ChessProgramState *cps)\r
9833 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9834       EngineOptionsPopup(savedHwnd, cps);\r
9835 }\r
9836 \r
9837 int flock(int fid, int code)\r
9838 {\r
9839     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
9840     OVERLAPPED ov;\r
9841     ov.hEvent = NULL;\r
9842     ov.Offset = 0;\r
9843     ov.OffsetHigh = 0;\r
9844     switch(code) {\r
9845       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
9846       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
9847       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
9848       default: return -1;\r
9849     }\r
9850     return 0;\r
9851 }\r