Implement Tournament Options dialog WinBoard
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts. \r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 #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], *curName;\r
893 \r
894     if(appData.autoLogo) {\r
895           curName = UserName();\r
896           if(strcmp(curName, oldUserName)) {\r
897             snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
898                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
899                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
900                 if(userLogo == NULL)\r
901                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
902           }\r
903     }\r
904 }\r
905 \r
906 BOOL\r
907 InitApplication(HINSTANCE hInstance)\r
908 {\r
909   WNDCLASS wc;\r
910 \r
911   /* Fill in window class structure with parameters that describe the */\r
912   /* main window. */\r
913 \r
914   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
915   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
916   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
917   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
918   wc.hInstance     = hInstance;         /* Owner of this class */\r
919   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
920   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
921   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
922   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
923   wc.lpszClassName = szAppName;                 /* Name to register as */\r
924 \r
925   /* Register the window class and return success/failure code. */\r
926   if (!RegisterClass(&wc)) return FALSE;\r
927 \r
928   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
929   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
930   wc.cbClsExtra    = 0;\r
931   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
932   wc.hInstance     = hInstance;\r
933   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
934   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
935   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
936   wc.lpszMenuName  = NULL;\r
937   wc.lpszClassName = szConsoleName;\r
938 \r
939   if (!RegisterClass(&wc)) return FALSE;\r
940   return TRUE;\r
941 }\r
942 \r
943 \r
944 /* Set by InitInstance, used by EnsureOnScreen */\r
945 int screenHeight, screenWidth;\r
946 \r
947 void\r
948 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
949 {\r
950 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
951   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
952   if (*x > screenWidth - 32) *x = 0;\r
953   if (*y > screenHeight - 32) *y = 0;\r
954   if (*x < minX) *x = minX;\r
955   if (*y < minY) *y = minY;\r
956 }\r
957 \r
958 VOID\r
959 LoadLogo(ChessProgramState *cps, int n)\r
960 {\r
961   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
962       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
963 \r
964       if (cps->programLogo == NULL && appData.debugMode) {\r
965           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
966       }\r
967   } else if(appData.autoLogo) {\r
968       if(appData.firstDirectory && appData.directory[n][0]) {\r
969         char buf[MSG_SIZ];\r
970           snprintf(buf, MSG_SIZ, "%s/logo.bmp", appData.directory[n]);\r
971         cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
972       }\r
973   }\r
974 }\r
975 \r
976 BOOL\r
977 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
978 {\r
979   HWND hwnd; /* Main window handle. */\r
980   int ibs;\r
981   WINDOWPLACEMENT wp;\r
982   char *filepart;\r
983 \r
984   hInst = hInstance;    /* Store instance handle in our global variable */\r
985   programName = szAppName;\r
986 \r
987   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
988     *filepart = NULLCHAR;\r
989   } else {\r
990     GetCurrentDirectory(MSG_SIZ, installDir);\r
991   }\r
992   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
993   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
994   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
995   /* xboard, and older WinBoards, controlled the move sound with the\r
996      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
997      always turn the option on (so that the backend will call us),\r
998      then let the user turn the sound off by setting it to silence if\r
999      desired.  To accommodate old winboard.ini files saved by old\r
1000      versions of WinBoard, we also turn off the sound if the option\r
1001      was initially set to false. [HGM] taken out of InitAppData */\r
1002   if (!appData.ringBellAfterMoves) {\r
1003     sounds[(int)SoundMove].name = strdup("");\r
1004     appData.ringBellAfterMoves = TRUE;\r
1005   }\r
1006   if (appData.debugMode) {\r
1007     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1008     setbuf(debugFP, NULL);\r
1009   }\r
1010 \r
1011   LoadLanguageFile(appData.language);\r
1012 \r
1013   InitBackEnd1();\r
1014 \r
1015 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1016 //  InitEngineUCI( installDir, &second );\r
1017 \r
1018   /* Create a main window for this application instance. */\r
1019   hwnd = CreateWindow(szAppName, szTitle,\r
1020                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1021                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1022                       NULL, NULL, hInstance, NULL);\r
1023   hwndMain = hwnd;\r
1024 \r
1025   /* If window could not be created, return "failure" */\r
1026   if (!hwnd) {\r
1027     return (FALSE);\r
1028   }\r
1029 \r
1030   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1031   LoadLogo(&first, 0);\r
1032 \r
1033   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
1034       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1035 \r
1036       if (second.programLogo == NULL && appData.debugMode) {\r
1037           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
1038       }\r
1039   } else if(appData.autoLogo) {\r
1040       char buf[MSG_SIZ];\r
1041       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
1042         snprintf(buf, MSG_SIZ, "logos\\%s.bmp", appData.icsHost);\r
1043         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1044       } else\r
1045       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
1046         snprintf(buf, MSG_SIZ, "%s\\logo.bmp", appData.secondDirectory);\r
1047         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
1048       }\r
1049   }\r
1050 \r
1051   SetUserLogo();\r
1052 \r
1053   iconWhite = LoadIcon(hInstance, "icon_white");\r
1054   iconBlack = LoadIcon(hInstance, "icon_black");\r
1055   iconCurrent = iconWhite;\r
1056   InitDrawingColors();\r
1057   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1058   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1059   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1060     /* Compute window size for each board size, and use the largest\r
1061        size that fits on this screen as the default. */\r
1062     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1063     if (boardSize == (BoardSize)-1 &&\r
1064         winH <= screenHeight\r
1065            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1066         && winW <= screenWidth) {\r
1067       boardSize = (BoardSize)ibs;\r
1068     }\r
1069   }\r
1070 \r
1071   InitDrawingSizes(boardSize, 0);\r
1072   InitMenuChecks();\r
1073   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1074 \r
1075   /* [AS] Load textures if specified */\r
1076   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1077   \r
1078   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1079       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1080       liteBackTextureMode = appData.liteBackTextureMode;\r
1081 \r
1082       if (liteBackTexture == NULL && appData.debugMode) {\r
1083           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1084       }\r
1085   }\r
1086   \r
1087   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1088       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1089       darkBackTextureMode = appData.darkBackTextureMode;\r
1090 \r
1091       if (darkBackTexture == NULL && appData.debugMode) {\r
1092           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1093       }\r
1094   }\r
1095 \r
1096   mysrandom( (unsigned) time(NULL) );\r
1097 \r
1098   /* [AS] Restore layout */\r
1099   if( wpMoveHistory.visible ) {\r
1100       MoveHistoryPopUp();\r
1101   }\r
1102 \r
1103   if( wpEvalGraph.visible ) {\r
1104       EvalGraphPopUp();\r
1105   }\r
1106 \r
1107   if( wpEngineOutput.visible ) {\r
1108       EngineOutputPopUp();\r
1109   }\r
1110 \r
1111   /* Make the window visible; update its client area; and return "success" */\r
1112   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1113   wp.length = sizeof(WINDOWPLACEMENT);\r
1114   wp.flags = 0;\r
1115   wp.showCmd = nCmdShow;\r
1116   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1117   wp.rcNormalPosition.left = wpMain.x;\r
1118   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1119   wp.rcNormalPosition.top = wpMain.y;\r
1120   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1121   SetWindowPlacement(hwndMain, &wp);\r
1122 \r
1123   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1124 \r
1125   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1126                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1127 \r
1128   if (hwndConsole) {\r
1129 #if AOT_CONSOLE\r
1130     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1131                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1132 #endif\r
1133     ShowWindow(hwndConsole, nCmdShow);\r
1134     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
1135       char buf[MSG_SIZ], *p = buf, *q;\r
1136         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
1137       do {\r
1138         q = strchr(p, ';');\r
1139         if(q) *q++ = 0;\r
1140         if(*p) ChatPopUp(p);\r
1141       } while(p=q);\r
1142     }\r
1143     SetActiveWindow(hwndConsole);\r
1144   }\r
1145   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1146   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1147 \r
1148   return TRUE;\r
1149 \r
1150 }\r
1151 \r
1152 VOID\r
1153 InitMenuChecks()\r
1154 {\r
1155   HMENU hmenu = GetMenu(hwndMain);\r
1156 \r
1157   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1158                         MF_BYCOMMAND|((appData.icsActive &&\r
1159                                        *appData.icsCommPort != NULLCHAR) ?\r
1160                                       MF_ENABLED : MF_GRAYED));\r
1161   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1162                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1163                                      MF_CHECKED : MF_UNCHECKED));\r
1164 }\r
1165 \r
1166 //---------------------------------------------------------------------------------------------------------\r
1167 \r
1168 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1169 #define XBOARD FALSE\r
1170 \r
1171 #define OPTCHAR "/"\r
1172 #define SEPCHAR "="\r
1173 \r
1174 #include "args.h"\r
1175 \r
1176 // front-end part of option handling\r
1177 \r
1178 VOID\r
1179 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1180 {\r
1181   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1182   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1183   DeleteDC(hdc);\r
1184   lf->lfWidth = 0;\r
1185   lf->lfEscapement = 0;\r
1186   lf->lfOrientation = 0;\r
1187   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1188   lf->lfItalic = mfp->italic;\r
1189   lf->lfUnderline = mfp->underline;\r
1190   lf->lfStrikeOut = mfp->strikeout;\r
1191   lf->lfCharSet = mfp->charset;\r
1192   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1193   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1194   lf->lfQuality = DEFAULT_QUALITY;\r
1195   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1196     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1197 }\r
1198 \r
1199 void\r
1200 CreateFontInMF(MyFont *mf)\r
1201\r
1202   LFfromMFP(&mf->lf, &mf->mfp);\r
1203   if (mf->hf) DeleteObject(mf->hf);\r
1204   mf->hf = CreateFontIndirect(&mf->lf);\r
1205 }\r
1206 \r
1207 // [HGM] This platform-dependent table provides the location for storing the color info\r
1208 void *\r
1209 colorVariable[] = {\r
1210   &whitePieceColor, \r
1211   &blackPieceColor, \r
1212   &lightSquareColor,\r
1213   &darkSquareColor, \r
1214   &highlightSquareColor,\r
1215   &premoveHighlightColor,\r
1216   NULL,\r
1217   &consoleBackgroundColor,\r
1218   &appData.fontForeColorWhite,\r
1219   &appData.fontBackColorWhite,\r
1220   &appData.fontForeColorBlack,\r
1221   &appData.fontBackColorBlack,\r
1222   &appData.evalHistColorWhite,\r
1223   &appData.evalHistColorBlack,\r
1224   &appData.highlightArrowColor,\r
1225 };\r
1226 \r
1227 /* Command line font name parser.  NULL name means do nothing.\r
1228    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1229    For backward compatibility, syntax without the colon is also\r
1230    accepted, but font names with digits in them won't work in that case.\r
1231 */\r
1232 VOID\r
1233 ParseFontName(char *name, MyFontParams *mfp)\r
1234 {\r
1235   char *p, *q;\r
1236   if (name == NULL) return;\r
1237   p = name;\r
1238   q = strchr(p, ':');\r
1239   if (q) {\r
1240     if (q - p >= sizeof(mfp->faceName))\r
1241       ExitArgError(_("Font name too long:"), name);\r
1242     memcpy(mfp->faceName, p, q - p);\r
1243     mfp->faceName[q - p] = NULLCHAR;\r
1244     p = q + 1;\r
1245   } else {\r
1246     q = mfp->faceName;\r
1247     while (*p && !isdigit(*p)) {\r
1248       *q++ = *p++;\r
1249       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1250         ExitArgError(_("Font name too long:"), name);\r
1251     }\r
1252     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1253     *q = NULLCHAR;\r
1254   }\r
1255   if (!*p) ExitArgError(_("Font point size missing:"), name);\r
1256   mfp->pointSize = (float) atof(p);\r
1257   mfp->bold = (strchr(p, 'b') != NULL);\r
1258   mfp->italic = (strchr(p, 'i') != NULL);\r
1259   mfp->underline = (strchr(p, 'u') != NULL);\r
1260   mfp->strikeout = (strchr(p, 's') != NULL);\r
1261   mfp->charset = DEFAULT_CHARSET;\r
1262   q = strchr(p, 'c');\r
1263   if (q)\r
1264     mfp->charset = (BYTE) atoi(q+1);\r
1265 }\r
1266 \r
1267 void\r
1268 ParseFont(char *name, int number)\r
1269 { // wrapper to shield back-end from 'font'\r
1270   ParseFontName(name, &font[boardSize][number]->mfp);\r
1271 }\r
1272 \r
1273 void\r
1274 SetFontDefaults()\r
1275 { // in WB  we have a 2D array of fonts; this initializes their description\r
1276   int i, j;\r
1277   /* Point font array elements to structures and\r
1278      parse default font names */\r
1279   for (i=0; i<NUM_FONTS; i++) {\r
1280     for (j=0; j<NUM_SIZES; j++) {\r
1281       font[j][i] = &fontRec[j][i];\r
1282       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1283     }\r
1284   }\r
1285 }\r
1286 \r
1287 void\r
1288 CreateFonts()\r
1289 { // here we create the actual fonts from the selected descriptions\r
1290   int i, j;\r
1291   for (i=0; i<NUM_FONTS; i++) {\r
1292     for (j=0; j<NUM_SIZES; j++) {\r
1293       CreateFontInMF(font[j][i]);\r
1294     }\r
1295   }\r
1296 }\r
1297 /* Color name parser.\r
1298    X version accepts X color names, but this one\r
1299    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1300 COLORREF\r
1301 ParseColorName(char *name)\r
1302 {\r
1303   int red, green, blue, count;\r
1304   char buf[MSG_SIZ];\r
1305 \r
1306   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1307   if (count != 3) {\r
1308     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1309       &red, &green, &blue);\r
1310   }\r
1311   if (count != 3) {\r
1312     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1313     DisplayError(buf, 0);\r
1314     return RGB(0, 0, 0);\r
1315   }\r
1316   return PALETTERGB(red, green, blue);\r
1317 }\r
1318 \r
1319 void\r
1320 ParseColor(int n, char *name)\r
1321 { // for WinBoard the color is an int, which needs to be derived from the string\r
1322   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1323 }\r
1324 \r
1325 void\r
1326 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1327 {\r
1328   char *e = argValue;\r
1329   int eff = 0;\r
1330 \r
1331   while (*e) {\r
1332     if (*e == 'b')      eff |= CFE_BOLD;\r
1333     else if (*e == 'i') eff |= CFE_ITALIC;\r
1334     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1335     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1336     else if (*e == '#' || isdigit(*e)) break;\r
1337     e++;\r
1338   }\r
1339   *effects = eff;\r
1340   *color   = ParseColorName(e);\r
1341 }\r
1342 \r
1343 void\r
1344 ParseTextAttribs(ColorClass cc, char *s)\r
1345 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1346     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1347     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1348 }\r
1349 \r
1350 void\r
1351 ParseBoardSize(void *addr, char *name)\r
1352 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1353   BoardSize bs = SizeTiny;\r
1354   while (sizeInfo[bs].name != NULL) {\r
1355     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1356         *(BoardSize *)addr = bs;\r
1357         return;\r
1358     }\r
1359     bs++;\r
1360   }\r
1361   ExitArgError(_("Unrecognized board size value"), name);\r
1362 }\r
1363 \r
1364 void\r
1365 LoadAllSounds()\r
1366 { // [HGM] import name from appData first\r
1367   ColorClass cc;\r
1368   SoundClass sc;\r
1369   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1370     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1371     textAttribs[cc].sound.data = NULL;\r
1372     MyLoadSound(&textAttribs[cc].sound);\r
1373   }\r
1374   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1375     textAttribs[cc].sound.name = strdup("");\r
1376     textAttribs[cc].sound.data = NULL;\r
1377   }\r
1378   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1379     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1380     sounds[sc].data = NULL;\r
1381     MyLoadSound(&sounds[sc]);\r
1382   }\r
1383 }\r
1384 \r
1385 void\r
1386 SetCommPortDefaults()\r
1387 {\r
1388    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1389   dcb.DCBlength = sizeof(DCB);\r
1390   dcb.BaudRate = 9600;\r
1391   dcb.fBinary = TRUE;\r
1392   dcb.fParity = FALSE;\r
1393   dcb.fOutxCtsFlow = FALSE;\r
1394   dcb.fOutxDsrFlow = FALSE;\r
1395   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1396   dcb.fDsrSensitivity = FALSE;\r
1397   dcb.fTXContinueOnXoff = TRUE;\r
1398   dcb.fOutX = FALSE;\r
1399   dcb.fInX = FALSE;\r
1400   dcb.fNull = FALSE;\r
1401   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1402   dcb.fAbortOnError = FALSE;\r
1403   dcb.ByteSize = 7;\r
1404   dcb.Parity = SPACEPARITY;\r
1405   dcb.StopBits = ONESTOPBIT;\r
1406 }\r
1407 \r
1408 // [HGM] args: these three cases taken out to stay in front-end\r
1409 void\r
1410 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1411 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1412         // while the curent board size determines the element. This system should be ported to XBoard.\r
1413         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1414         int bs;\r
1415         for (bs=0; bs<NUM_SIZES; bs++) {\r
1416           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1417           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1418           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1419             ad->argName, mfp->faceName, mfp->pointSize,\r
1420             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1421             mfp->bold ? "b" : "",\r
1422             mfp->italic ? "i" : "",\r
1423             mfp->underline ? "u" : "",\r
1424             mfp->strikeout ? "s" : "",\r
1425             (int)mfp->charset);\r
1426         }\r
1427       }\r
1428 \r
1429 void\r
1430 ExportSounds()\r
1431 { // [HGM] copy the names from the internal WB variables to appData\r
1432   ColorClass cc;\r
1433   SoundClass sc;\r
1434   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1435     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1436   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1437     (&appData.soundMove)[sc] = sounds[sc].name;\r
1438 }\r
1439 \r
1440 void\r
1441 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1442 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1443         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1444         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1445           (ta->effects & CFE_BOLD) ? "b" : "",\r
1446           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1447           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1448           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1449           (ta->effects) ? " " : "",\r
1450           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1451       }\r
1452 \r
1453 void\r
1454 SaveColor(FILE *f, ArgDescriptor *ad)\r
1455 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1456         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1457         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1458           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1459 }\r
1460 \r
1461 void\r
1462 SaveBoardSize(FILE *f, char *name, void *addr)\r
1463 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1464   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1465 }\r
1466 \r
1467 void\r
1468 ParseCommPortSettings(char *s)\r
1469 { // wrapper to keep dcb from back-end\r
1470   ParseCommSettings(s, &dcb);\r
1471 }\r
1472 \r
1473 void\r
1474 GetWindowCoords()\r
1475 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1476   GetActualPlacement(hwndMain, &wpMain);\r
1477   GetActualPlacement(hwndConsole, &wpConsole);\r
1478   GetActualPlacement(commentDialog, &wpComment);\r
1479   GetActualPlacement(editTagsDialog, &wpTags);\r
1480   GetActualPlacement(gameListDialog, &wpGameList);\r
1481   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1482   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1483   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1484 }\r
1485 \r
1486 void\r
1487 PrintCommPortSettings(FILE *f, char *name)\r
1488 { // wrapper to shield back-end from DCB\r
1489       PrintCommSettings(f, name, &dcb);\r
1490 }\r
1491 \r
1492 int\r
1493 MySearchPath(char *installDir, char *name, char *fullname)\r
1494 {\r
1495   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1496   if(name[0]== '%') {\r
1497     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1498     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1499       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1500       *strchr(buf, '%') = 0;\r
1501       strcat(fullname, getenv(buf));\r
1502       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1503     }\r
1504     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1505     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1506     return (int) strlen(fullname);\r
1507   }\r
1508   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1509 }\r
1510 \r
1511 int\r
1512 MyGetFullPathName(char *name, char *fullname)\r
1513 {\r
1514   char *dummy;\r
1515   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1516 }\r
1517 \r
1518 int\r
1519 MainWindowUp()\r
1520 { // [HGM] args: allows testing if main window is realized from back-end\r
1521   return hwndMain != NULL;\r
1522 }\r
1523 \r
1524 void\r
1525 PopUpStartupDialog()\r
1526 {\r
1527     FARPROC lpProc;\r
1528     \r
1529     LoadLanguageFile(appData.language);\r
1530     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1531     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1532     FreeProcInstance(lpProc);\r
1533 }\r
1534 \r
1535 /*---------------------------------------------------------------------------*\\r
1536  *\r
1537  * GDI board drawing routines\r
1538  *\r
1539 \*---------------------------------------------------------------------------*/\r
1540 \r
1541 /* [AS] Draw square using background texture */\r
1542 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1543 {\r
1544     XFORM   x;\r
1545 \r
1546     if( mode == 0 ) {\r
1547         return; /* Should never happen! */\r
1548     }\r
1549 \r
1550     SetGraphicsMode( dst, GM_ADVANCED );\r
1551 \r
1552     switch( mode ) {\r
1553     case 1:\r
1554         /* Identity */\r
1555         break;\r
1556     case 2:\r
1557         /* X reflection */\r
1558         x.eM11 = -1.0;\r
1559         x.eM12 = 0;\r
1560         x.eM21 = 0;\r
1561         x.eM22 = 1.0;\r
1562         x.eDx = (FLOAT) dw + dx - 1;\r
1563         x.eDy = 0;\r
1564         dx = 0;\r
1565         SetWorldTransform( dst, &x );\r
1566         break;\r
1567     case 3:\r
1568         /* Y reflection */\r
1569         x.eM11 = 1.0;\r
1570         x.eM12 = 0;\r
1571         x.eM21 = 0;\r
1572         x.eM22 = -1.0;\r
1573         x.eDx = 0;\r
1574         x.eDy = (FLOAT) dh + dy - 1;\r
1575         dy = 0;\r
1576         SetWorldTransform( dst, &x );\r
1577         break;\r
1578     case 4:\r
1579         /* X/Y flip */\r
1580         x.eM11 = 0;\r
1581         x.eM12 = 1.0;\r
1582         x.eM21 = 1.0;\r
1583         x.eM22 = 0;\r
1584         x.eDx = (FLOAT) dx;\r
1585         x.eDy = (FLOAT) dy;\r
1586         dx = 0;\r
1587         dy = 0;\r
1588         SetWorldTransform( dst, &x );\r
1589         break;\r
1590     }\r
1591 \r
1592     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1593 \r
1594     x.eM11 = 1.0;\r
1595     x.eM12 = 0;\r
1596     x.eM21 = 0;\r
1597     x.eM22 = 1.0;\r
1598     x.eDx = 0;\r
1599     x.eDy = 0;\r
1600     SetWorldTransform( dst, &x );\r
1601 \r
1602     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1603 }\r
1604 \r
1605 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1606 enum {\r
1607     PM_WP = (int) WhitePawn, \r
1608     PM_WN = (int) WhiteKnight, \r
1609     PM_WB = (int) WhiteBishop, \r
1610     PM_WR = (int) WhiteRook, \r
1611     PM_WQ = (int) WhiteQueen, \r
1612     PM_WF = (int) WhiteFerz, \r
1613     PM_WW = (int) WhiteWazir, \r
1614     PM_WE = (int) WhiteAlfil, \r
1615     PM_WM = (int) WhiteMan, \r
1616     PM_WO = (int) WhiteCannon, \r
1617     PM_WU = (int) WhiteUnicorn, \r
1618     PM_WH = (int) WhiteNightrider, \r
1619     PM_WA = (int) WhiteAngel, \r
1620     PM_WC = (int) WhiteMarshall, \r
1621     PM_WAB = (int) WhiteCardinal, \r
1622     PM_WD = (int) WhiteDragon, \r
1623     PM_WL = (int) WhiteLance, \r
1624     PM_WS = (int) WhiteCobra, \r
1625     PM_WV = (int) WhiteFalcon, \r
1626     PM_WSG = (int) WhiteSilver, \r
1627     PM_WG = (int) WhiteGrasshopper, \r
1628     PM_WK = (int) WhiteKing,\r
1629     PM_BP = (int) BlackPawn, \r
1630     PM_BN = (int) BlackKnight, \r
1631     PM_BB = (int) BlackBishop, \r
1632     PM_BR = (int) BlackRook, \r
1633     PM_BQ = (int) BlackQueen, \r
1634     PM_BF = (int) BlackFerz, \r
1635     PM_BW = (int) BlackWazir, \r
1636     PM_BE = (int) BlackAlfil, \r
1637     PM_BM = (int) BlackMan,\r
1638     PM_BO = (int) BlackCannon, \r
1639     PM_BU = (int) BlackUnicorn, \r
1640     PM_BH = (int) BlackNightrider, \r
1641     PM_BA = (int) BlackAngel, \r
1642     PM_BC = (int) BlackMarshall, \r
1643     PM_BG = (int) BlackGrasshopper, \r
1644     PM_BAB = (int) BlackCardinal,\r
1645     PM_BD = (int) BlackDragon,\r
1646     PM_BL = (int) BlackLance,\r
1647     PM_BS = (int) BlackCobra,\r
1648     PM_BV = (int) BlackFalcon,\r
1649     PM_BSG = (int) BlackSilver,\r
1650     PM_BK = (int) BlackKing\r
1651 };\r
1652 \r
1653 static HFONT hPieceFont = NULL;\r
1654 static HBITMAP hPieceMask[(int) EmptySquare];\r
1655 static HBITMAP hPieceFace[(int) EmptySquare];\r
1656 static int fontBitmapSquareSize = 0;\r
1657 static char pieceToFontChar[(int) EmptySquare] =\r
1658                               { 'p', 'n', 'b', 'r', 'q', \r
1659                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1660                       'k', 'o', 'm', 'v', 't', 'w', \r
1661                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1662                                                               'l' };\r
1663 \r
1664 extern BOOL SetCharTable( char *table, const char * map );\r
1665 /* [HGM] moved to backend.c */\r
1666 \r
1667 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1668 {\r
1669     HBRUSH hbrush;\r
1670     BYTE r1 = GetRValue( color );\r
1671     BYTE g1 = GetGValue( color );\r
1672     BYTE b1 = GetBValue( color );\r
1673     BYTE r2 = r1 / 2;\r
1674     BYTE g2 = g1 / 2;\r
1675     BYTE b2 = b1 / 2;\r
1676     RECT rc;\r
1677 \r
1678     /* Create a uniform background first */\r
1679     hbrush = CreateSolidBrush( color );\r
1680     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1681     FillRect( hdc, &rc, hbrush );\r
1682     DeleteObject( hbrush );\r
1683     \r
1684     if( mode == 1 ) {\r
1685         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1686         int steps = squareSize / 2;\r
1687         int i;\r
1688 \r
1689         for( i=0; i<steps; i++ ) {\r
1690             BYTE r = r1 - (r1-r2) * i / steps;\r
1691             BYTE g = g1 - (g1-g2) * i / steps;\r
1692             BYTE b = b1 - (b1-b2) * i / steps;\r
1693 \r
1694             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1695             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1696             FillRect( hdc, &rc, hbrush );\r
1697             DeleteObject(hbrush);\r
1698         }\r
1699     }\r
1700     else if( mode == 2 ) {\r
1701         /* Diagonal gradient, good more or less for every piece */\r
1702         POINT triangle[3];\r
1703         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1704         HBRUSH hbrush_old;\r
1705         int steps = squareSize;\r
1706         int i;\r
1707 \r
1708         triangle[0].x = squareSize - steps;\r
1709         triangle[0].y = squareSize;\r
1710         triangle[1].x = squareSize;\r
1711         triangle[1].y = squareSize;\r
1712         triangle[2].x = squareSize;\r
1713         triangle[2].y = squareSize - steps;\r
1714 \r
1715         for( i=0; i<steps; i++ ) {\r
1716             BYTE r = r1 - (r1-r2) * i / steps;\r
1717             BYTE g = g1 - (g1-g2) * i / steps;\r
1718             BYTE b = b1 - (b1-b2) * i / steps;\r
1719 \r
1720             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1721             hbrush_old = SelectObject( hdc, hbrush );\r
1722             Polygon( hdc, triangle, 3 );\r
1723             SelectObject( hdc, hbrush_old );\r
1724             DeleteObject(hbrush);\r
1725             triangle[0].x++;\r
1726             triangle[2].y++;\r
1727         }\r
1728 \r
1729         SelectObject( hdc, hpen );\r
1730     }\r
1731 }\r
1732 \r
1733 /*\r
1734     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1735     seems to work ok. The main problem here is to find the "inside" of a chess\r
1736     piece: follow the steps as explained below.\r
1737 */\r
1738 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1739 {\r
1740     HBITMAP hbm;\r
1741     HBITMAP hbm_old;\r
1742     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1743     RECT rc;\r
1744     SIZE sz;\r
1745     POINT pt;\r
1746     int backColor = whitePieceColor; \r
1747     int foreColor = blackPieceColor;\r
1748     \r
1749     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1750         backColor = appData.fontBackColorWhite;\r
1751         foreColor = appData.fontForeColorWhite;\r
1752     }\r
1753     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1754         backColor = appData.fontBackColorBlack;\r
1755         foreColor = appData.fontForeColorBlack;\r
1756     }\r
1757 \r
1758     /* Mask */\r
1759     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1760 \r
1761     hbm_old = SelectObject( hdc, hbm );\r
1762 \r
1763     rc.left = 0;\r
1764     rc.top = 0;\r
1765     rc.right = squareSize;\r
1766     rc.bottom = squareSize;\r
1767 \r
1768     /* Step 1: background is now black */\r
1769     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1770 \r
1771     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1772 \r
1773     pt.x = (squareSize - sz.cx) / 2;\r
1774     pt.y = (squareSize - sz.cy) / 2;\r
1775 \r
1776     SetBkMode( hdc, TRANSPARENT );\r
1777     SetTextColor( hdc, chroma );\r
1778     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1779     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1780 \r
1781     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1782     /* Step 3: the area outside the piece is filled with white */\r
1783 //    FloodFill( hdc, 0, 0, chroma );\r
1784     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1785     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1786     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1787     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1788     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1789     /* \r
1790         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1791         but if the start point is not inside the piece we're lost!\r
1792         There should be a better way to do this... if we could create a region or path\r
1793         from the fill operation we would be fine for example.\r
1794     */\r
1795 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1796     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1797 \r
1798     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1799         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1800         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1801 \r
1802         SelectObject( dc2, bm2 );\r
1803         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1804         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1805         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1806         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1807         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1808 \r
1809         DeleteDC( dc2 );\r
1810         DeleteObject( bm2 );\r
1811     }\r
1812 \r
1813     SetTextColor( hdc, 0 );\r
1814     /* \r
1815         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1816         draw the piece again in black for safety.\r
1817     */\r
1818     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1819 \r
1820     SelectObject( hdc, hbm_old );\r
1821 \r
1822     if( hPieceMask[index] != NULL ) {\r
1823         DeleteObject( hPieceMask[index] );\r
1824     }\r
1825 \r
1826     hPieceMask[index] = hbm;\r
1827 \r
1828     /* Face */\r
1829     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1830 \r
1831     SelectObject( hdc, hbm );\r
1832 \r
1833     {\r
1834         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1835         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1836         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1837 \r
1838         SelectObject( dc1, hPieceMask[index] );\r
1839         SelectObject( dc2, bm2 );\r
1840         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1841         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1842         \r
1843         /* \r
1844             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1845             the piece background and deletes (makes transparent) the rest.\r
1846             Thanks to that mask, we are free to paint the background with the greates\r
1847             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1848             We use this, to make gradients and give the pieces a "roundish" look.\r
1849         */\r
1850         SetPieceBackground( hdc, backColor, 2 );\r
1851         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1852 \r
1853         DeleteDC( dc2 );\r
1854         DeleteDC( dc1 );\r
1855         DeleteObject( bm2 );\r
1856     }\r
1857 \r
1858     SetTextColor( hdc, foreColor );\r
1859     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1860 \r
1861     SelectObject( hdc, hbm_old );\r
1862 \r
1863     if( hPieceFace[index] != NULL ) {\r
1864         DeleteObject( hPieceFace[index] );\r
1865     }\r
1866 \r
1867     hPieceFace[index] = hbm;\r
1868 }\r
1869 \r
1870 static int TranslatePieceToFontPiece( int piece )\r
1871 {\r
1872     switch( piece ) {\r
1873     case BlackPawn:\r
1874         return PM_BP;\r
1875     case BlackKnight:\r
1876         return PM_BN;\r
1877     case BlackBishop:\r
1878         return PM_BB;\r
1879     case BlackRook:\r
1880         return PM_BR;\r
1881     case BlackQueen:\r
1882         return PM_BQ;\r
1883     case BlackKing:\r
1884         return PM_BK;\r
1885     case WhitePawn:\r
1886         return PM_WP;\r
1887     case WhiteKnight:\r
1888         return PM_WN;\r
1889     case WhiteBishop:\r
1890         return PM_WB;\r
1891     case WhiteRook:\r
1892         return PM_WR;\r
1893     case WhiteQueen:\r
1894         return PM_WQ;\r
1895     case WhiteKing:\r
1896         return PM_WK;\r
1897 \r
1898     case BlackAngel:\r
1899         return PM_BA;\r
1900     case BlackMarshall:\r
1901         return PM_BC;\r
1902     case BlackFerz:\r
1903         return PM_BF;\r
1904     case BlackNightrider:\r
1905         return PM_BH;\r
1906     case BlackAlfil:\r
1907         return PM_BE;\r
1908     case BlackWazir:\r
1909         return PM_BW;\r
1910     case BlackUnicorn:\r
1911         return PM_BU;\r
1912     case BlackCannon:\r
1913         return PM_BO;\r
1914     case BlackGrasshopper:\r
1915         return PM_BG;\r
1916     case BlackMan:\r
1917         return PM_BM;\r
1918     case BlackSilver:\r
1919         return PM_BSG;\r
1920     case BlackLance:\r
1921         return PM_BL;\r
1922     case BlackFalcon:\r
1923         return PM_BV;\r
1924     case BlackCobra:\r
1925         return PM_BS;\r
1926     case BlackCardinal:\r
1927         return PM_BAB;\r
1928     case BlackDragon:\r
1929         return PM_BD;\r
1930 \r
1931     case WhiteAngel:\r
1932         return PM_WA;\r
1933     case WhiteMarshall:\r
1934         return PM_WC;\r
1935     case WhiteFerz:\r
1936         return PM_WF;\r
1937     case WhiteNightrider:\r
1938         return PM_WH;\r
1939     case WhiteAlfil:\r
1940         return PM_WE;\r
1941     case WhiteWazir:\r
1942         return PM_WW;\r
1943     case WhiteUnicorn:\r
1944         return PM_WU;\r
1945     case WhiteCannon:\r
1946         return PM_WO;\r
1947     case WhiteGrasshopper:\r
1948         return PM_WG;\r
1949     case WhiteMan:\r
1950         return PM_WM;\r
1951     case WhiteSilver:\r
1952         return PM_WSG;\r
1953     case WhiteLance:\r
1954         return PM_WL;\r
1955     case WhiteFalcon:\r
1956         return PM_WV;\r
1957     case WhiteCobra:\r
1958         return PM_WS;\r
1959     case WhiteCardinal:\r
1960         return PM_WAB;\r
1961     case WhiteDragon:\r
1962         return PM_WD;\r
1963     }\r
1964 \r
1965     return 0;\r
1966 }\r
1967 \r
1968 void CreatePiecesFromFont()\r
1969 {\r
1970     LOGFONT lf;\r
1971     HDC hdc_window = NULL;\r
1972     HDC hdc = NULL;\r
1973     HFONT hfont_old;\r
1974     int fontHeight;\r
1975     int i;\r
1976 \r
1977     if( fontBitmapSquareSize < 0 ) {\r
1978         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
1979         return;\r
1980     }\r
1981 \r
1982     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
1983         fontBitmapSquareSize = -1;\r
1984         return;\r
1985     }\r
1986 \r
1987     if( fontBitmapSquareSize != squareSize ) {\r
1988         hdc_window = GetDC( hwndMain );\r
1989         hdc = CreateCompatibleDC( hdc_window );\r
1990 \r
1991         if( hPieceFont != NULL ) {\r
1992             DeleteObject( hPieceFont );\r
1993         }\r
1994         else {\r
1995             for( i=0; i<=(int)BlackKing; i++ ) {\r
1996                 hPieceMask[i] = NULL;\r
1997                 hPieceFace[i] = NULL;\r
1998             }\r
1999         }\r
2000 \r
2001         fontHeight = 75;\r
2002 \r
2003         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2004             fontHeight = appData.fontPieceSize;\r
2005         }\r
2006 \r
2007         fontHeight = (fontHeight * squareSize) / 100;\r
2008 \r
2009         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2010         lf.lfWidth = 0;\r
2011         lf.lfEscapement = 0;\r
2012         lf.lfOrientation = 0;\r
2013         lf.lfWeight = FW_NORMAL;\r
2014         lf.lfItalic = 0;\r
2015         lf.lfUnderline = 0;\r
2016         lf.lfStrikeOut = 0;\r
2017         lf.lfCharSet = DEFAULT_CHARSET;\r
2018         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2019         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2020         lf.lfQuality = PROOF_QUALITY;\r
2021         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2022         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2023         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2024 \r
2025         hPieceFont = CreateFontIndirect( &lf );\r
2026 \r
2027         if( hPieceFont == NULL ) {\r
2028             fontBitmapSquareSize = -2;\r
2029         }\r
2030         else {\r
2031             /* Setup font-to-piece character table */\r
2032             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2033                 /* No (or wrong) global settings, try to detect the font */\r
2034                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2035                     /* Alpha */\r
2036                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2037                 }\r
2038                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2039                     /* DiagramTT* family */\r
2040                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2041                 }\r
2042                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2043                     /* Fairy symbols */\r
2044                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2045                 }\r
2046                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2047                     /* Good Companion (Some characters get warped as literal :-( */\r
2048                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2049                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2050                     SetCharTable(pieceToFontChar, s);\r
2051                 }\r
2052                 else {\r
2053                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2054                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2055                 }\r
2056             }\r
2057 \r
2058             /* Create bitmaps */\r
2059             hfont_old = SelectObject( hdc, hPieceFont );\r
2060             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2061                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2062                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2063 \r
2064             SelectObject( hdc, hfont_old );\r
2065 \r
2066             fontBitmapSquareSize = squareSize;\r
2067         }\r
2068     }\r
2069 \r
2070     if( hdc != NULL ) {\r
2071         DeleteDC( hdc );\r
2072     }\r
2073 \r
2074     if( hdc_window != NULL ) {\r
2075         ReleaseDC( hwndMain, hdc_window );\r
2076     }\r
2077 }\r
2078 \r
2079 HBITMAP\r
2080 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2081 {\r
2082   char name[128];\r
2083 \r
2084     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2085   if (gameInfo.event &&\r
2086       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2087       strcmp(name, "k80s") == 0) {\r
2088     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2089   }\r
2090   return LoadBitmap(hinst, name);\r
2091 }\r
2092 \r
2093 \r
2094 /* Insert a color into the program's logical palette\r
2095    structure.  This code assumes the given color is\r
2096    the result of the RGB or PALETTERGB macro, and it\r
2097    knows how those macros work (which is documented).\r
2098 */\r
2099 VOID\r
2100 InsertInPalette(COLORREF color)\r
2101 {\r
2102   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2103 \r
2104   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2105     DisplayFatalError(_("Too many colors"), 0, 1);\r
2106     pLogPal->palNumEntries--;\r
2107     return;\r
2108   }\r
2109 \r
2110   pe->peFlags = (char) 0;\r
2111   pe->peRed = (char) (0xFF & color);\r
2112   pe->peGreen = (char) (0xFF & (color >> 8));\r
2113   pe->peBlue = (char) (0xFF & (color >> 16));\r
2114   return;\r
2115 }\r
2116 \r
2117 \r
2118 VOID\r
2119 InitDrawingColors()\r
2120 {\r
2121   if (pLogPal == NULL) {\r
2122     /* Allocate enough memory for a logical palette with\r
2123      * PALETTESIZE entries and set the size and version fields\r
2124      * of the logical palette structure.\r
2125      */\r
2126     pLogPal = (NPLOGPALETTE)\r
2127       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2128                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2129     pLogPal->palVersion    = 0x300;\r
2130   }\r
2131   pLogPal->palNumEntries = 0;\r
2132 \r
2133   InsertInPalette(lightSquareColor);\r
2134   InsertInPalette(darkSquareColor);\r
2135   InsertInPalette(whitePieceColor);\r
2136   InsertInPalette(blackPieceColor);\r
2137   InsertInPalette(highlightSquareColor);\r
2138   InsertInPalette(premoveHighlightColor);\r
2139 \r
2140   /*  create a logical color palette according the information\r
2141    *  in the LOGPALETTE structure.\r
2142    */\r
2143   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2144 \r
2145   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2146   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2147   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2148   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2149   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2150   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2151   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2152   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2153   /* [AS] Force rendering of the font-based pieces */\r
2154   if( fontBitmapSquareSize > 0 ) {\r
2155     fontBitmapSquareSize = 0;\r
2156   }\r
2157 }\r
2158 \r
2159 \r
2160 int\r
2161 BoardWidth(int boardSize, int n)\r
2162 { /* [HGM] argument n added to allow different width and height */\r
2163   int lineGap = sizeInfo[boardSize].lineGap;\r
2164 \r
2165   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2166       lineGap = appData.overrideLineGap;\r
2167   }\r
2168 \r
2169   return (n + 1) * lineGap +\r
2170           n * sizeInfo[boardSize].squareSize;\r
2171 }\r
2172 \r
2173 /* Respond to board resize by dragging edge */\r
2174 VOID\r
2175 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2176 {\r
2177   BoardSize newSize = NUM_SIZES - 1;\r
2178   static int recurse = 0;\r
2179   if (IsIconic(hwndMain)) return;\r
2180   if (recurse > 0) return;\r
2181   recurse++;\r
2182   while (newSize > 0) {\r
2183         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2184         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2185            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2186     newSize--;\r
2187   } \r
2188   boardSize = newSize;\r
2189   InitDrawingSizes(boardSize, flags);\r
2190   recurse--;\r
2191 }\r
2192 \r
2193 \r
2194 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2195 \r
2196 VOID\r
2197 InitDrawingSizes(BoardSize boardSize, int flags)\r
2198 {\r
2199   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2200   ChessSquare piece;\r
2201   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2202   HDC hdc;\r
2203   SIZE clockSize, messageSize;\r
2204   HFONT oldFont;\r
2205   char buf[MSG_SIZ];\r
2206   char *str;\r
2207   HMENU hmenu = GetMenu(hwndMain);\r
2208   RECT crect, wrect, oldRect;\r
2209   int offby;\r
2210   LOGBRUSH logbrush;\r
2211 \r
2212   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2213   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2214 \r
2215   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2216   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2217 \r
2218   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2219   oldRect.top = wpMain.y;\r
2220   oldRect.right = wpMain.x + wpMain.width;\r
2221   oldRect.bottom = wpMain.y + wpMain.height;\r
2222 \r
2223   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2224   smallLayout = sizeInfo[boardSize].smallLayout;\r
2225   squareSize = sizeInfo[boardSize].squareSize;\r
2226   lineGap = sizeInfo[boardSize].lineGap;\r
2227   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2228 \r
2229   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2230       lineGap = appData.overrideLineGap;\r
2231   }\r
2232 \r
2233   if (tinyLayout != oldTinyLayout) {\r
2234     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2235     if (tinyLayout) {\r
2236       style &= ~WS_SYSMENU;\r
2237       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2238                  "&Minimize\tCtrl+F4");\r
2239     } else {\r
2240       style |= WS_SYSMENU;\r
2241       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2242     }\r
2243     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2244 \r
2245     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2246       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2247         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2248     }\r
2249     DrawMenuBar(hwndMain);\r
2250   }\r
2251 \r
2252   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2253   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2254 \r
2255   /* Get text area sizes */\r
2256   hdc = GetDC(hwndMain);\r
2257   if (appData.clockMode) {\r
2258     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2259   } else {\r
2260     snprintf(buf, MSG_SIZ, _("White"));\r
2261   }\r
2262   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2263   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2264   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2265   str = _("We only care about the height here");\r
2266   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2267   SelectObject(hdc, oldFont);\r
2268   ReleaseDC(hwndMain, hdc);\r
2269 \r
2270   /* Compute where everything goes */\r
2271   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2272         /* [HGM] logo: if either logo is on, reserve space for it */\r
2273         logoHeight =  2*clockSize.cy;\r
2274         leftLogoRect.left   = OUTER_MARGIN;\r
2275         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2276         leftLogoRect.top    = OUTER_MARGIN;\r
2277         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2278 \r
2279         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2280         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2281         rightLogoRect.top    = OUTER_MARGIN;\r
2282         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2283 \r
2284 \r
2285     whiteRect.left = leftLogoRect.right;\r
2286     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2287     whiteRect.top = OUTER_MARGIN;\r
2288     whiteRect.bottom = whiteRect.top + logoHeight;\r
2289 \r
2290     blackRect.right = rightLogoRect.left;\r
2291     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2292     blackRect.top = whiteRect.top;\r
2293     blackRect.bottom = whiteRect.bottom;\r
2294   } else {\r
2295     whiteRect.left = OUTER_MARGIN;\r
2296     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2297     whiteRect.top = OUTER_MARGIN;\r
2298     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2299 \r
2300     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2301     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2302     blackRect.top = whiteRect.top;\r
2303     blackRect.bottom = whiteRect.bottom;\r
2304 \r
2305     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2306   }\r
2307 \r
2308   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2309   if (appData.showButtonBar) {\r
2310     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2311       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2312   } else {\r
2313     messageRect.right = OUTER_MARGIN + boardWidth;\r
2314   }\r
2315   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2316   messageRect.bottom = messageRect.top + messageSize.cy;\r
2317 \r
2318   boardRect.left = OUTER_MARGIN;\r
2319   boardRect.right = boardRect.left + boardWidth;\r
2320   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2321   boardRect.bottom = boardRect.top + boardHeight;\r
2322 \r
2323   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2324   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2325   oldBoardSize = boardSize;\r
2326   oldTinyLayout = tinyLayout;\r
2327   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2328   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2329     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2330   winW *= 1 + twoBoards;\r
2331   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2332   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2333   wpMain.height = winH; //       without disturbing window attachments\r
2334   GetWindowRect(hwndMain, &wrect);\r
2335   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2336                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2337 \r
2338   // [HGM] placement: let attached windows follow size change.\r
2339   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2340   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2341   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2342   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2343   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2344 \r
2345   /* compensate if menu bar wrapped */\r
2346   GetClientRect(hwndMain, &crect);\r
2347   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2348   wpMain.height += offby;\r
2349   switch (flags) {\r
2350   case WMSZ_TOPLEFT:\r
2351     SetWindowPos(hwndMain, NULL, \r
2352                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2353                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2354     break;\r
2355 \r
2356   case WMSZ_TOPRIGHT:\r
2357   case WMSZ_TOP:\r
2358     SetWindowPos(hwndMain, NULL, \r
2359                  wrect.left, wrect.bottom - wpMain.height, \r
2360                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2361     break;\r
2362 \r
2363   case WMSZ_BOTTOMLEFT:\r
2364   case WMSZ_LEFT:\r
2365     SetWindowPos(hwndMain, NULL, \r
2366                  wrect.right - wpMain.width, wrect.top, \r
2367                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2368     break;\r
2369 \r
2370   case WMSZ_BOTTOMRIGHT:\r
2371   case WMSZ_BOTTOM:\r
2372   case WMSZ_RIGHT:\r
2373   default:\r
2374     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2375                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2376     break;\r
2377   }\r
2378 \r
2379   hwndPause = NULL;\r
2380   for (i = 0; i < N_BUTTONS; i++) {\r
2381     if (buttonDesc[i].hwnd != NULL) {\r
2382       DestroyWindow(buttonDesc[i].hwnd);\r
2383       buttonDesc[i].hwnd = NULL;\r
2384     }\r
2385     if (appData.showButtonBar) {\r
2386       buttonDesc[i].hwnd =\r
2387         CreateWindow("BUTTON", buttonDesc[i].label,\r
2388                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2389                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2390                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2391                      (HMENU) buttonDesc[i].id,\r
2392                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2393       if (tinyLayout) {\r
2394         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2395                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2396                     MAKELPARAM(FALSE, 0));\r
2397       }\r
2398       if (buttonDesc[i].id == IDM_Pause)\r
2399         hwndPause = buttonDesc[i].hwnd;\r
2400       buttonDesc[i].wndproc = (WNDPROC)\r
2401         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2402     }\r
2403   }\r
2404   if (gridPen != NULL) DeleteObject(gridPen);\r
2405   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2406   if (premovePen != NULL) DeleteObject(premovePen);\r
2407   if (lineGap != 0) {\r
2408     logbrush.lbStyle = BS_SOLID;\r
2409     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2410     gridPen =\r
2411       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2412                    lineGap, &logbrush, 0, NULL);\r
2413     logbrush.lbColor = highlightSquareColor;\r
2414     highlightPen =\r
2415       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2416                    lineGap, &logbrush, 0, NULL);\r
2417 \r
2418     logbrush.lbColor = premoveHighlightColor; \r
2419     premovePen =\r
2420       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2421                    lineGap, &logbrush, 0, NULL);\r
2422 \r
2423     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2424     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2425       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2426       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2427         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2428       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2429         BOARD_WIDTH * (squareSize + lineGap);\r
2430       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2431     }\r
2432     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2433       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2434       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2435         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2436         lineGap / 2 + (i * (squareSize + lineGap));\r
2437       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2438         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2439       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2440     }\r
2441   }\r
2442 \r
2443   /* [HGM] Licensing requirement */\r
2444 #ifdef GOTHIC\r
2445   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2446 #endif\r
2447 #ifdef FALCON\r
2448   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2449 #endif\r
2450   GothicPopUp( "", VariantNormal);\r
2451 \r
2452 \r
2453 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2454 \r
2455   /* Load piece bitmaps for this board size */\r
2456   for (i=0; i<=2; i++) {\r
2457     for (piece = WhitePawn;\r
2458          (int) piece < (int) BlackPawn;\r
2459          piece = (ChessSquare) ((int) piece + 1)) {\r
2460       if (pieceBitmap[i][piece] != NULL)\r
2461         DeleteObject(pieceBitmap[i][piece]);\r
2462     }\r
2463   }\r
2464 \r
2465   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2466   // Orthodox Chess pieces\r
2467   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2468   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2469   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2470   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2471   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2472   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2473   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2474   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2475   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2476   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2477   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2478   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2479   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2480   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2481   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2482   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2483     // in Shogi, Hijack the unused Queen for Lance\r
2484     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2485     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2486     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2487   } else {\r
2488     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2489     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2490     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2491   }\r
2492 \r
2493   if(squareSize <= 72 && squareSize >= 33) { \r
2494     /* A & C are available in most sizes now */\r
2495     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2496       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2497       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2498       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2499       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2500       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2501       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2502       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2503       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2504       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2505       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2506       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2507       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2508     } else { // Smirf-like\r
2509       if(gameInfo.variant == VariantSChess) {\r
2510         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2511         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2512         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2513       } else {\r
2514         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2515         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2516         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2517       }\r
2518     }\r
2519     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2520       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2521       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2522       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2523     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2524       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2525       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2526       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2527     } else { // WinBoard standard\r
2528       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2529       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2530       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2531     }\r
2532   }\r
2533 \r
2534 \r
2535   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2536     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2537     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2538     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2539     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2540     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2541     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2542     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2543     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2544     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2545     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2546     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2547     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2548     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2549     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2550     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2551     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2552     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2553     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2554     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2555     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2556     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2557     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2558     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2559     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2560     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2561     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2562     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2563     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2564     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2565     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2566 \r
2567     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2568       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2569       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2570       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2571       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2572       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2573       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2574       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2575       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2576       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2577       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2578       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2579       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2580     } else {\r
2581       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2582       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2583       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2584       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2585       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2586       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2587       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2588       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2589       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2590       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2591       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2592       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2593     }\r
2594 \r
2595   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2596     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2597     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2598     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2599     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2600     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2601     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2602     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2603     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2604     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2605     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2606     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2607     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2608     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2609     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2610   }\r
2611 \r
2612 \r
2613   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2614   /* special Shogi support in this size */\r
2615   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2616       for (piece = WhitePawn;\r
2617            (int) piece < (int) BlackPawn;\r
2618            piece = (ChessSquare) ((int) piece + 1)) {\r
2619         if (pieceBitmap[i][piece] != NULL)\r
2620           DeleteObject(pieceBitmap[i][piece]);\r
2621       }\r
2622     }\r
2623   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2624   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2625   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2626   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2627   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2628   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2629   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2630   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2631   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2632   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2633   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2634   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2635   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2636   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2637   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2638   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2639   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2640   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2641   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2642   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2643   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2644   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2645   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2646   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2647   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2648   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2649   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2650   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2651   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2652   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2653   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2654   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2655   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2656   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2657   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2658   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2659   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2660   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2661   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2662   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2663   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2664   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2665   minorSize = 0;\r
2666   }\r
2667 }\r
2668 \r
2669 HBITMAP\r
2670 PieceBitmap(ChessSquare p, int kind)\r
2671 {\r
2672   if ((int) p >= (int) BlackPawn)\r
2673     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2674 \r
2675   return pieceBitmap[kind][(int) p];\r
2676 }\r
2677 \r
2678 /***************************************************************/\r
2679 \r
2680 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2681 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2682 /*\r
2683 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2684 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2685 */\r
2686 \r
2687 VOID\r
2688 SquareToPos(int row, int column, int * x, int * y)\r
2689 {\r
2690   if (flipView) {\r
2691     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2692     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2693   } else {\r
2694     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2695     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2696   }\r
2697 }\r
2698 \r
2699 VOID\r
2700 DrawCoordsOnDC(HDC hdc)\r
2701 {\r
2702   static char files[24] = {'0', '1','2','3','4','5','6','7','8','9','0','1','1','0','9','8','7','6','5','4','3','2','1','0'};\r
2703   static char ranks[24] = {'l', 'k','j','i','h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h','i','j','k','l'};\r
2704   char str[2] = { NULLCHAR, NULLCHAR };\r
2705   int oldMode, oldAlign, x, y, start, i;\r
2706   HFONT oldFont;\r
2707   HBRUSH oldBrush;\r
2708 \r
2709   if (!appData.showCoords)\r
2710     return;\r
2711 \r
2712   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
2713 \r
2714   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2715   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2716   oldAlign = GetTextAlign(hdc);\r
2717   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2718 \r
2719   y = boardRect.top + lineGap;\r
2720   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2721 \r
2722   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2723   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2724     str[0] = files[start + i];\r
2725     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2726     y += squareSize + lineGap;\r
2727   }\r
2728 \r
2729   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
2730 \r
2731   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2732   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2733     str[0] = ranks[start + i];\r
2734     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2735     x += squareSize + lineGap;\r
2736   }    \r
2737 \r
2738   SelectObject(hdc, oldBrush);\r
2739   SetBkMode(hdc, oldMode);\r
2740   SetTextAlign(hdc, oldAlign);\r
2741   SelectObject(hdc, oldFont);\r
2742 }\r
2743 \r
2744 VOID\r
2745 DrawGridOnDC(HDC hdc)\r
2746 {\r
2747   HPEN oldPen;\r
2748  \r
2749   if (lineGap != 0) {\r
2750     oldPen = SelectObject(hdc, gridPen);\r
2751     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2752     SelectObject(hdc, oldPen);\r
2753   }\r
2754 }\r
2755 \r
2756 #define HIGHLIGHT_PEN 0\r
2757 #define PREMOVE_PEN   1\r
2758 \r
2759 VOID\r
2760 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2761 {\r
2762   int x1, y1;\r
2763   HPEN oldPen, hPen;\r
2764   if (lineGap == 0) return;\r
2765   if (flipView) {\r
2766     x1 = boardRect.left +\r
2767       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2768     y1 = boardRect.top +\r
2769       lineGap/2 + y * (squareSize + lineGap);\r
2770   } else {\r
2771     x1 = boardRect.left +\r
2772       lineGap/2 + x * (squareSize + lineGap);\r
2773     y1 = boardRect.top +\r
2774       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2775   }\r
2776   hPen = pen ? premovePen : highlightPen;\r
2777   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2778   MoveToEx(hdc, x1, y1, NULL);\r
2779   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2780   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2781   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2782   LineTo(hdc, x1, y1);\r
2783   SelectObject(hdc, oldPen);\r
2784 }\r
2785 \r
2786 VOID\r
2787 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2788 {\r
2789   int i;\r
2790   for (i=0; i<2; i++) {\r
2791     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2792       DrawHighlightOnDC(hdc, TRUE,\r
2793                         h->sq[i].x, h->sq[i].y,\r
2794                         pen);\r
2795   }\r
2796 }\r
2797 \r
2798 /* Note: sqcolor is used only in monoMode */\r
2799 /* Note that this code is largely duplicated in woptions.c,\r
2800    function DrawSampleSquare, so that needs to be updated too */\r
2801 VOID\r
2802 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2803 {\r
2804   HBITMAP oldBitmap;\r
2805   HBRUSH oldBrush;\r
2806   int tmpSize;\r
2807 \r
2808   if (appData.blindfold) return;\r
2809 \r
2810   /* [AS] Use font-based pieces if needed */\r
2811   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2812     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2813     CreatePiecesFromFont();\r
2814 \r
2815     if( fontBitmapSquareSize == squareSize ) {\r
2816         int index = TranslatePieceToFontPiece(piece);\r
2817 \r
2818         SelectObject( tmphdc, hPieceMask[ index ] );\r
2819 \r
2820       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2821         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2822       else\r
2823         BitBlt( hdc,\r
2824             x, y,\r
2825             squareSize, squareSize,\r
2826             tmphdc,\r
2827             0, 0,\r
2828             SRCAND );\r
2829 \r
2830         SelectObject( tmphdc, hPieceFace[ index ] );\r
2831 \r
2832       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2833         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2834       else\r
2835         BitBlt( hdc,\r
2836             x, y,\r
2837             squareSize, squareSize,\r
2838             tmphdc,\r
2839             0, 0,\r
2840             SRCPAINT );\r
2841 \r
2842         return;\r
2843     }\r
2844   }\r
2845 \r
2846   if (appData.monoMode) {\r
2847     SelectObject(tmphdc, PieceBitmap(piece, \r
2848       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2849     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2850            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2851   } else {\r
2852     tmpSize = squareSize;\r
2853     if(minorSize &&\r
2854         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2855          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2856       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2857       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2858       x += (squareSize - minorSize)>>1;\r
2859       y += squareSize - minorSize - 2;\r
2860       tmpSize = minorSize;\r
2861     }\r
2862     if (color || appData.allWhite ) {\r
2863       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2864       if( color )\r
2865               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2866       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2867       if(appData.upsideDown && color==flipView)\r
2868         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2869       else\r
2870         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2871       /* Use black for outline of white pieces */\r
2872       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2873       if(appData.upsideDown && color==flipView)\r
2874         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2875       else\r
2876         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2877     } else {\r
2878       /* Use square color for details of black pieces */\r
2879       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2880       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2881       if(appData.upsideDown && !flipView)\r
2882         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2883       else\r
2884         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2885     }\r
2886     SelectObject(hdc, oldBrush);\r
2887     SelectObject(tmphdc, oldBitmap);\r
2888   }\r
2889 }\r
2890 \r
2891 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2892 int GetBackTextureMode( int algo )\r
2893 {\r
2894     int result = BACK_TEXTURE_MODE_DISABLED;\r
2895 \r
2896     switch( algo ) \r
2897     {\r
2898         case BACK_TEXTURE_MODE_PLAIN:\r
2899             result = 1; /* Always use identity map */\r
2900             break;\r
2901         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2902             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2903             break;\r
2904     }\r
2905 \r
2906     return result;\r
2907 }\r
2908 \r
2909 /* \r
2910     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2911     to handle redraws cleanly (as random numbers would always be different).\r
2912 */\r
2913 VOID RebuildTextureSquareInfo()\r
2914 {\r
2915     BITMAP bi;\r
2916     int lite_w = 0;\r
2917     int lite_h = 0;\r
2918     int dark_w = 0;\r
2919     int dark_h = 0;\r
2920     int row;\r
2921     int col;\r
2922 \r
2923     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2924 \r
2925     if( liteBackTexture != NULL ) {\r
2926         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2927             lite_w = bi.bmWidth;\r
2928             lite_h = bi.bmHeight;\r
2929         }\r
2930     }\r
2931 \r
2932     if( darkBackTexture != NULL ) {\r
2933         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2934             dark_w = bi.bmWidth;\r
2935             dark_h = bi.bmHeight;\r
2936         }\r
2937     }\r
2938 \r
2939     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2940         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2941             if( (col + row) & 1 ) {\r
2942                 /* Lite square */\r
2943                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2944                   if( lite_w >= squareSize*BOARD_WIDTH )\r
2945                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
2946                   else\r
2947                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2948                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
2949                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
2950                   else\r
2951                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2952                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2953                 }\r
2954             }\r
2955             else {\r
2956                 /* Dark square */\r
2957                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
2958                   if( dark_w >= squareSize*BOARD_WIDTH )\r
2959                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
2960                   else\r
2961                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
2962                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
2963                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
2964                   else\r
2965                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
2966                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
2967                 }\r
2968             }\r
2969         }\r
2970     }\r
2971 }\r
2972 \r
2973 /* [AS] Arrow highlighting support */\r
2974 \r
2975 static double A_WIDTH = 5; /* Width of arrow body */\r
2976 \r
2977 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
2978 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
2979 \r
2980 static double Sqr( double x )\r
2981 {\r
2982     return x*x;\r
2983 }\r
2984 \r
2985 static int Round( double x )\r
2986 {\r
2987     return (int) (x + 0.5);\r
2988 }\r
2989 \r
2990 /* Draw an arrow between two points using current settings */\r
2991 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
2992 {\r
2993     POINT arrow[7];\r
2994     double dx, dy, j, k, x, y;\r
2995 \r
2996     if( d_x == s_x ) {\r
2997         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2998 \r
2999         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3000         arrow[0].y = s_y;\r
3001 \r
3002         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3003         arrow[1].y = d_y - h;\r
3004 \r
3005         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3006         arrow[2].y = d_y - h;\r
3007 \r
3008         arrow[3].x = d_x;\r
3009         arrow[3].y = d_y;\r
3010 \r
3011         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3012         arrow[5].y = d_y - h;\r
3013 \r
3014         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3015         arrow[4].y = d_y - h;\r
3016 \r
3017         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3018         arrow[6].y = s_y;\r
3019     }\r
3020     else if( d_y == s_y ) {\r
3021         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3022 \r
3023         arrow[0].x = s_x;\r
3024         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3025 \r
3026         arrow[1].x = d_x - w;\r
3027         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3028 \r
3029         arrow[2].x = d_x - w;\r
3030         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3031 \r
3032         arrow[3].x = d_x;\r
3033         arrow[3].y = d_y;\r
3034 \r
3035         arrow[5].x = d_x - w;\r
3036         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3037 \r
3038         arrow[4].x = d_x - w;\r
3039         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3040 \r
3041         arrow[6].x = s_x;\r
3042         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3043     }\r
3044     else {\r
3045         /* [AS] Needed a lot of paper for this! :-) */\r
3046         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3047         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3048   \r
3049         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3050 \r
3051         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3052 \r
3053         x = s_x;\r
3054         y = s_y;\r
3055 \r
3056         arrow[0].x = Round(x - j);\r
3057         arrow[0].y = Round(y + j*dx);\r
3058 \r
3059         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3060         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3061 \r
3062         if( d_x > s_x ) {\r
3063             x = (double) d_x - k;\r
3064             y = (double) d_y - k*dy;\r
3065         }\r
3066         else {\r
3067             x = (double) d_x + k;\r
3068             y = (double) d_y + k*dy;\r
3069         }\r
3070 \r
3071         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3072 \r
3073         arrow[6].x = Round(x - j);\r
3074         arrow[6].y = Round(y + j*dx);\r
3075 \r
3076         arrow[2].x = Round(arrow[6].x + 2*j);\r
3077         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3078 \r
3079         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3080         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3081 \r
3082         arrow[4].x = d_x;\r
3083         arrow[4].y = d_y;\r
3084 \r
3085         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3086         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3087     }\r
3088 \r
3089     Polygon( hdc, arrow, 7 );\r
3090 }\r
3091 \r
3092 /* [AS] Draw an arrow between two squares */\r
3093 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3094 {\r
3095     int s_x, s_y, d_x, d_y;\r
3096     HPEN hpen;\r
3097     HPEN holdpen;\r
3098     HBRUSH hbrush;\r
3099     HBRUSH holdbrush;\r
3100     LOGBRUSH stLB;\r
3101 \r
3102     if( s_col == d_col && s_row == d_row ) {\r
3103         return;\r
3104     }\r
3105 \r
3106     /* Get source and destination points */\r
3107     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3108     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3109 \r
3110     if( d_y > s_y ) {\r
3111         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3112     }\r
3113     else if( d_y < s_y ) {\r
3114         d_y += squareSize / 2 + squareSize / 4;\r
3115     }\r
3116     else {\r
3117         d_y += squareSize / 2;\r
3118     }\r
3119 \r
3120     if( d_x > s_x ) {\r
3121         d_x += squareSize / 2 - squareSize / 4;\r
3122     }\r
3123     else if( d_x < s_x ) {\r
3124         d_x += squareSize / 2 + squareSize / 4;\r
3125     }\r
3126     else {\r
3127         d_x += squareSize / 2;\r
3128     }\r
3129 \r
3130     s_x += squareSize / 2;\r
3131     s_y += squareSize / 2;\r
3132 \r
3133     /* Adjust width */\r
3134     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3135 \r
3136     /* Draw */\r
3137     stLB.lbStyle = BS_SOLID;\r
3138     stLB.lbColor = appData.highlightArrowColor;\r
3139     stLB.lbHatch = 0;\r
3140 \r
3141     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3142     holdpen = SelectObject( hdc, hpen );\r
3143     hbrush = CreateBrushIndirect( &stLB );\r
3144     holdbrush = SelectObject( hdc, hbrush );\r
3145 \r
3146     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3147 \r
3148     SelectObject( hdc, holdpen );\r
3149     SelectObject( hdc, holdbrush );\r
3150     DeleteObject( hpen );\r
3151     DeleteObject( hbrush );\r
3152 }\r
3153 \r
3154 BOOL HasHighlightInfo()\r
3155 {\r
3156     BOOL result = FALSE;\r
3157 \r
3158     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3159         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3160     {\r
3161         result = TRUE;\r
3162     }\r
3163 \r
3164     return result;\r
3165 }\r
3166 \r
3167 BOOL IsDrawArrowEnabled()\r
3168 {\r
3169     BOOL result = FALSE;\r
3170 \r
3171     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3172         result = TRUE;\r
3173     }\r
3174 \r
3175     return result;\r
3176 }\r
3177 \r
3178 VOID DrawArrowHighlight( HDC hdc )\r
3179 {\r
3180     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3181         DrawArrowBetweenSquares( hdc,\r
3182             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3183             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3184     }\r
3185 }\r
3186 \r
3187 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3188 {\r
3189     HRGN result = NULL;\r
3190 \r
3191     if( HasHighlightInfo() ) {\r
3192         int x1, y1, x2, y2;\r
3193         int sx, sy, dx, dy;\r
3194 \r
3195         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3196         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3197 \r
3198         sx = MIN( x1, x2 );\r
3199         sy = MIN( y1, y2 );\r
3200         dx = MAX( x1, x2 ) + squareSize;\r
3201         dy = MAX( y1, y2 ) + squareSize;\r
3202 \r
3203         result = CreateRectRgn( sx, sy, dx, dy );\r
3204     }\r
3205 \r
3206     return result;\r
3207 }\r
3208 \r
3209 /*\r
3210     Warning: this function modifies the behavior of several other functions. \r
3211     \r
3212     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3213     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3214     repaint is scattered all over the place, which is not good for features such as\r
3215     "arrow highlighting" that require a full repaint of the board.\r
3216 \r
3217     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3218     user interaction, when speed is not so important) but especially to avoid errors\r
3219     in the displayed graphics.\r
3220 \r
3221     In such patched places, I always try refer to this function so there is a single\r
3222     place to maintain knowledge.\r
3223     \r
3224     To restore the original behavior, just return FALSE unconditionally.\r
3225 */\r
3226 BOOL IsFullRepaintPreferrable()\r
3227 {\r
3228     BOOL result = FALSE;\r
3229 \r
3230     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3231         /* Arrow may appear on the board */\r
3232         result = TRUE;\r
3233     }\r
3234 \r
3235     return result;\r
3236 }\r
3237 \r
3238 /* \r
3239     This function is called by DrawPosition to know whether a full repaint must\r
3240     be forced or not.\r
3241 \r
3242     Only DrawPosition may directly call this function, which makes use of \r
3243     some state information. Other function should call DrawPosition specifying \r
3244     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3245 */\r
3246 BOOL DrawPositionNeedsFullRepaint()\r
3247 {\r
3248     BOOL result = FALSE;\r
3249 \r
3250     /* \r
3251         Probably a slightly better policy would be to trigger a full repaint\r
3252         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3253         but animation is fast enough that it's difficult to notice.\r
3254     */\r
3255     if( animInfo.piece == EmptySquare ) {\r
3256         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3257             result = TRUE;\r
3258         }\r
3259     }\r
3260 \r
3261     return result;\r
3262 }\r
3263 \r
3264 VOID\r
3265 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3266 {\r
3267   int row, column, x, y, square_color, piece_color;\r
3268   ChessSquare piece;\r
3269   HBRUSH oldBrush;\r
3270   HDC texture_hdc = NULL;\r
3271 \r
3272   /* [AS] Initialize background textures if needed */\r
3273   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3274       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3275       if( backTextureSquareSize != squareSize \r
3276        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3277           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3278           backTextureSquareSize = squareSize;\r
3279           RebuildTextureSquareInfo();\r
3280       }\r
3281 \r
3282       texture_hdc = CreateCompatibleDC( hdc );\r
3283   }\r
3284 \r
3285   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3286     for (column = 0; column < BOARD_WIDTH; column++) {\r
3287   \r
3288       SquareToPos(row, column, &x, &y);\r
3289 \r
3290       piece = board[row][column];\r
3291 \r
3292       square_color = ((column + row) % 2) == 1;\r
3293       if( gameInfo.variant == VariantXiangqi ) {\r
3294           square_color = !InPalace(row, column);\r
3295           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3296           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3297       }\r
3298       piece_color = (int) piece < (int) BlackPawn;\r
3299 \r
3300 \r
3301       /* [HGM] holdings file: light square or black */\r
3302       if(column == BOARD_LEFT-2) {\r
3303             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3304                 square_color = 1;\r
3305             else {\r
3306                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3307                 continue;\r
3308             }\r
3309       } else\r
3310       if(column == BOARD_RGHT + 1 ) {\r
3311             if( row < gameInfo.holdingsSize )\r
3312                 square_color = 1;\r
3313             else {\r
3314                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3315                 continue;\r
3316             }\r
3317       }\r
3318       if(column == BOARD_LEFT-1 ) /* left align */\r
3319             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3320       else if( column == BOARD_RGHT) /* right align */\r
3321             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3322       else\r
3323       if (appData.monoMode) {\r
3324         if (piece == EmptySquare) {\r
3325           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3326                  square_color ? WHITENESS : BLACKNESS);\r
3327         } else {\r
3328           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3329         }\r
3330       } \r
3331       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
3332           /* [AS] Draw the square using a texture bitmap */\r
3333           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3334           int r = row, c = column; // [HGM] do not flip board in flipView\r
3335           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3336 \r
3337           DrawTile( x, y, \r
3338               squareSize, squareSize, \r
3339               hdc, \r
3340               texture_hdc,\r
3341               backTextureSquareInfo[r][c].mode,\r
3342               backTextureSquareInfo[r][c].x,\r
3343               backTextureSquareInfo[r][c].y );\r
3344 \r
3345           SelectObject( texture_hdc, hbm );\r
3346 \r
3347           if (piece != EmptySquare) {\r
3348               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3349           }\r
3350       }\r
3351       else {\r
3352         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3353 \r
3354         oldBrush = SelectObject(hdc, brush );\r
3355         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3356         SelectObject(hdc, oldBrush);\r
3357         if (piece != EmptySquare)\r
3358           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3359       }\r
3360     }\r
3361   }\r
3362 \r
3363   if( texture_hdc != NULL ) {\r
3364     DeleteDC( texture_hdc );\r
3365   }\r
3366 }\r
3367 \r
3368 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3369 void fputDW(FILE *f, int x)\r
3370 {\r
3371         fputc(x     & 255, f);\r
3372         fputc(x>>8  & 255, f);\r
3373         fputc(x>>16 & 255, f);\r
3374         fputc(x>>24 & 255, f);\r
3375 }\r
3376 \r
3377 #define MAX_CLIPS 200   /* more than enough */\r
3378 \r
3379 VOID\r
3380 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3381 {\r
3382 //  HBITMAP bufferBitmap;\r
3383   BITMAP bi;\r
3384 //  RECT Rect;\r
3385   HDC tmphdc;\r
3386   HBITMAP hbm;\r
3387   int w = 100, h = 50;\r
3388 \r
3389   if(logo == NULL) return;\r
3390 //  GetClientRect(hwndMain, &Rect);\r
3391 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3392 //                                      Rect.bottom-Rect.top+1);\r
3393   tmphdc = CreateCompatibleDC(hdc);\r
3394   hbm = SelectObject(tmphdc, logo);\r
3395   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3396             w = bi.bmWidth;\r
3397             h = bi.bmHeight;\r
3398   }\r
3399   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3400                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3401   SelectObject(tmphdc, hbm);\r
3402   DeleteDC(tmphdc);\r
3403 }\r
3404 \r
3405 VOID\r
3406 DisplayLogos()\r
3407 {\r
3408   if(logoHeight) {\r
3409         HDC hdc = GetDC(hwndMain);\r
3410         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3411         if(appData.autoLogo) {\r
3412           \r
3413           switch(gameMode) { // pick logos based on game mode\r
3414             case IcsObserving:\r
3415                 whiteLogo = second.programLogo; // ICS logo\r
3416                 blackLogo = second.programLogo;\r
3417             default:\r
3418                 break;\r
3419             case IcsPlayingWhite:\r
3420                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3421                 blackLogo = second.programLogo; // ICS logo\r
3422                 break;\r
3423             case IcsPlayingBlack:\r
3424                 whiteLogo = second.programLogo; // ICS logo\r
3425                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3426                 break;\r
3427             case TwoMachinesPlay:\r
3428                 if(first.twoMachinesColor[0] == 'b') {\r
3429                     whiteLogo = second.programLogo;\r
3430                     blackLogo = first.programLogo;\r
3431                 }\r
3432                 break;\r
3433             case MachinePlaysWhite:\r
3434                 blackLogo = userLogo;\r
3435                 break;\r
3436             case MachinePlaysBlack:\r
3437                 whiteLogo = userLogo;\r
3438                 blackLogo = first.programLogo;\r
3439           }\r
3440         }\r
3441         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3442         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3443         ReleaseDC(hwndMain, hdc);\r
3444   }\r
3445 }\r
3446 \r
3447 static HDC hdcSeek;\r
3448 \r
3449 // [HGM] seekgraph\r
3450 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3451 {\r
3452     POINT stPt;\r
3453     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3454     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3455     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3456     SelectObject( hdcSeek, hp );\r
3457 }\r
3458 \r
3459 // front-end wrapper for drawing functions to do rectangles\r
3460 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3461 {\r
3462     HPEN hp;\r
3463     RECT rc;\r
3464 \r
3465     if (hdcSeek == NULL) {\r
3466     hdcSeek = GetDC(hwndMain);\r
3467       if (!appData.monoMode) {\r
3468         SelectPalette(hdcSeek, hPal, FALSE);\r
3469         RealizePalette(hdcSeek);\r
3470       }\r
3471     }\r
3472     hp = SelectObject( hdcSeek, gridPen );\r
3473     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3474     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3475     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3476     SelectObject( hdcSeek, hp );\r
3477 }\r
3478 \r
3479 // front-end wrapper for putting text in graph\r
3480 void DrawSeekText(char *buf, int x, int y)\r
3481 {\r
3482         SIZE stSize;\r
3483         SetBkMode( hdcSeek, TRANSPARENT );\r
3484         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3485         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3486 }\r
3487 \r
3488 void DrawSeekDot(int x, int y, int color)\r
3489 {\r
3490         int square = color & 0x80;\r
3491         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3492                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3493         color &= 0x7F;\r
3494         if(square)\r
3495             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3496                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3497         else\r
3498             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3499                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3500             SelectObject(hdcSeek, oldBrush);\r
3501 }\r
3502 \r
3503 VOID\r
3504 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3505 {\r
3506   static Board lastReq[2], lastDrawn[2];\r
3507   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3508   static int lastDrawnFlipView = 0;\r
3509   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3510   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3511   HDC tmphdc;\r
3512   HDC hdcmem;\r
3513   HBITMAP bufferBitmap;\r
3514   HBITMAP oldBitmap;\r
3515   RECT Rect;\r
3516   HRGN clips[MAX_CLIPS];\r
3517   ChessSquare dragged_piece = EmptySquare;\r
3518   int nr = twoBoards*partnerUp;\r
3519 \r
3520   /* I'm undecided on this - this function figures out whether a full\r
3521    * repaint is necessary on its own, so there's no real reason to have the\r
3522    * caller tell it that.  I think this can safely be set to FALSE - but\r
3523    * if we trust the callers not to request full repaints unnessesarily, then\r
3524    * we could skip some clipping work.  In other words, only request a full\r
3525    * redraw when the majority of pieces have changed positions (ie. flip, \r
3526    * gamestart and similar)  --Hawk\r
3527    */\r
3528   Boolean fullrepaint = repaint;\r
3529 \r
3530   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3531 \r
3532   if( DrawPositionNeedsFullRepaint() ) {\r
3533       fullrepaint = TRUE;\r
3534   }\r
3535 \r
3536   if (board == NULL) {\r
3537     if (!lastReqValid[nr]) {\r
3538       return;\r
3539     }\r
3540     board = lastReq[nr];\r
3541   } else {\r
3542     CopyBoard(lastReq[nr], board);\r
3543     lastReqValid[nr] = 1;\r
3544   }\r
3545 \r
3546   if (doingSizing) {\r
3547     return;\r
3548   }\r
3549 \r
3550   if (IsIconic(hwndMain)) {\r
3551     return;\r
3552   }\r
3553 \r
3554   if (hdc == NULL) {\r
3555     hdc = GetDC(hwndMain);\r
3556     if (!appData.monoMode) {\r
3557       SelectPalette(hdc, hPal, FALSE);\r
3558       RealizePalette(hdc);\r
3559     }\r
3560     releaseDC = TRUE;\r
3561   } else {\r
3562     releaseDC = FALSE;\r
3563   }\r
3564 \r
3565   /* Create some work-DCs */\r
3566   hdcmem = CreateCompatibleDC(hdc);\r
3567   tmphdc = CreateCompatibleDC(hdc);\r
3568 \r
3569   /* If dragging is in progress, we temporarely remove the piece */\r
3570   /* [HGM] or temporarily decrease count if stacked              */\r
3571   /*       !! Moved to before board compare !!                   */\r
3572   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3573     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3574     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3575             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3576         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3577     } else \r
3578     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3579             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3580         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3581     } else \r
3582         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3583   }\r
3584 \r
3585   /* Figure out which squares need updating by comparing the \r
3586    * newest board with the last drawn board and checking if\r
3587    * flipping has changed.\r
3588    */\r
3589   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3590     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3591       for (column = 0; column < BOARD_WIDTH; column++) {\r
3592         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3593           SquareToPos(row, column, &x, &y);\r
3594           clips[num_clips++] =\r
3595             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3596         }\r
3597       }\r
3598     }\r
3599    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3600     for (i=0; i<2; i++) {\r
3601       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3602           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3603         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3604             lastDrawnHighlight.sq[i].y >= 0) {\r
3605           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3606                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3607           clips[num_clips++] =\r
3608             CreateRectRgn(x - lineGap, y - lineGap, \r
3609                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3610         }\r
3611         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3612           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3613           clips[num_clips++] =\r
3614             CreateRectRgn(x - lineGap, y - lineGap, \r
3615                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3616         }\r
3617       }\r
3618     }\r
3619     for (i=0; i<2; i++) {\r
3620       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3621           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3622         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3623             lastDrawnPremove.sq[i].y >= 0) {\r
3624           SquareToPos(lastDrawnPremove.sq[i].y,\r
3625                       lastDrawnPremove.sq[i].x, &x, &y);\r
3626           clips[num_clips++] =\r
3627             CreateRectRgn(x - lineGap, y - lineGap, \r
3628                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3629         }\r
3630         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3631             premoveHighlightInfo.sq[i].y >= 0) {\r
3632           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3633                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3634           clips[num_clips++] =\r
3635             CreateRectRgn(x - lineGap, y - lineGap, \r
3636                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3637         }\r
3638       }\r
3639     }\r
3640    } else { // nr == 1\r
3641         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3642         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3643         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3644         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3645       for (i=0; i<2; i++) {\r
3646         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3647             partnerHighlightInfo.sq[i].y >= 0) {\r
3648           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3649                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3650           clips[num_clips++] =\r
3651             CreateRectRgn(x - lineGap, y - lineGap, \r
3652                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3653         }\r
3654         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3655             oldPartnerHighlight.sq[i].y >= 0) {\r
3656           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3657                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3658           clips[num_clips++] =\r
3659             CreateRectRgn(x - lineGap, y - lineGap, \r
3660                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3661         }\r
3662       }\r
3663    }\r
3664   } else {\r
3665     fullrepaint = TRUE;\r
3666   }\r
3667 \r
3668   /* Create a buffer bitmap - this is the actual bitmap\r
3669    * being written to.  When all the work is done, we can\r
3670    * copy it to the real DC (the screen).  This avoids\r
3671    * the problems with flickering.\r
3672    */\r
3673   GetClientRect(hwndMain, &Rect);\r
3674   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3675                                         Rect.bottom-Rect.top+1);\r
3676   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3677   if (!appData.monoMode) {\r
3678     SelectPalette(hdcmem, hPal, FALSE);\r
3679   }\r
3680 \r
3681   /* Create clips for dragging */\r
3682   if (!fullrepaint) {\r
3683     if (dragInfo.from.x >= 0) {\r
3684       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3685       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3686     }\r
3687     if (dragInfo.start.x >= 0) {\r
3688       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3689       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3690     }\r
3691     if (dragInfo.pos.x >= 0) {\r
3692       x = dragInfo.pos.x - squareSize / 2;\r
3693       y = dragInfo.pos.y - squareSize / 2;\r
3694       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3695     }\r
3696     if (dragInfo.lastpos.x >= 0) {\r
3697       x = dragInfo.lastpos.x - squareSize / 2;\r
3698       y = dragInfo.lastpos.y - squareSize / 2;\r
3699       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3700     }\r
3701   }\r
3702 \r
3703   /* Are we animating a move?  \r
3704    * If so, \r
3705    *   - remove the piece from the board (temporarely)\r
3706    *   - calculate the clipping region\r
3707    */\r
3708   if (!fullrepaint) {\r
3709     if (animInfo.piece != EmptySquare) {\r
3710       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3711       x = boardRect.left + animInfo.lastpos.x;\r
3712       y = boardRect.top + animInfo.lastpos.y;\r
3713       x2 = boardRect.left + animInfo.pos.x;\r
3714       y2 = boardRect.top + animInfo.pos.y;\r
3715       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3716       /* Slight kludge.  The real problem is that after AnimateMove is\r
3717          done, the position on the screen does not match lastDrawn.\r
3718          This currently causes trouble only on e.p. captures in\r
3719          atomic, where the piece moves to an empty square and then\r
3720          explodes.  The old and new positions both had an empty square\r
3721          at the destination, but animation has drawn a piece there and\r
3722          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3723       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3724     }\r
3725   }\r
3726 \r
3727   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3728   if (num_clips == 0)\r
3729     fullrepaint = TRUE;\r
3730 \r
3731   /* Set clipping on the memory DC */\r
3732   if (!fullrepaint) {\r
3733     SelectClipRgn(hdcmem, clips[0]);\r
3734     for (x = 1; x < num_clips; x++) {\r
3735       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3736         abort();  // this should never ever happen!\r
3737     }\r
3738   }\r
3739 \r
3740   /* Do all the drawing to the memory DC */\r
3741   if(explodeInfo.radius) { // [HGM] atomic\r
3742         HBRUSH oldBrush;\r
3743         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3744         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3745         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3746         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3747         x += squareSize/2;\r
3748         y += squareSize/2;\r
3749         if(!fullrepaint) {\r
3750           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3751           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3752         }\r
3753         DrawGridOnDC(hdcmem);\r
3754         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3755         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3756         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3757         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3758         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3759         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3760         SelectObject(hdcmem, oldBrush);\r
3761   } else {\r
3762     DrawGridOnDC(hdcmem);\r
3763     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3764         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3765         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3766     } else {\r
3767         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3768         oldPartnerHighlight = partnerHighlightInfo;\r
3769     }\r
3770     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3771   }\r
3772   if(nr == 0) // [HGM] dual: markers only on left board\r
3773   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3774     for (column = 0; column < BOARD_WIDTH; column++) {\r
3775         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3776             HBRUSH oldBrush = SelectObject(hdcmem, \r
3777                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3778             SquareToPos(row, column, &x, &y);\r
3779             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3780                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3781             SelectObject(hdcmem, oldBrush);\r
3782         }\r
3783     }\r
3784   }\r
3785 \r
3786   if( appData.highlightMoveWithArrow ) {\r
3787     DrawArrowHighlight(hdcmem);\r
3788   }\r
3789 \r
3790   DrawCoordsOnDC(hdcmem);\r
3791 \r
3792   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3793                  /* to make sure lastDrawn contains what is actually drawn */\r
3794 \r
3795   /* Put the dragged piece back into place and draw it (out of place!) */\r
3796     if (dragged_piece != EmptySquare) {\r
3797     /* [HGM] or restack */\r
3798     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3799                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3800     else\r
3801     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3802                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3803     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3804     x = dragInfo.pos.x - squareSize / 2;\r
3805     y = dragInfo.pos.y - squareSize / 2;\r
3806     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
3807                   ((int) dragInfo.piece < (int) BlackPawn), \r
3808                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3809   }   \r
3810   \r
3811   /* Put the animated piece back into place and draw it */\r
3812   if (animInfo.piece != EmptySquare) {\r
3813     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3814     x = boardRect.left + animInfo.pos.x;\r
3815     y = boardRect.top + animInfo.pos.y;\r
3816     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3817                   ((int) animInfo.piece < (int) BlackPawn),\r
3818                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3819   }\r
3820 \r
3821   /* Release the bufferBitmap by selecting in the old bitmap \r
3822    * and delete the memory DC\r
3823    */\r
3824   SelectObject(hdcmem, oldBitmap);\r
3825   DeleteDC(hdcmem);\r
3826 \r
3827   /* Set clipping on the target DC */\r
3828   if (!fullrepaint) {\r
3829     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3830         RECT rect;\r
3831         GetRgnBox(clips[x], &rect);\r
3832         DeleteObject(clips[x]);\r
3833         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3834                           rect.right + wpMain.width/2, rect.bottom);\r
3835     }\r
3836     SelectClipRgn(hdc, clips[0]);\r
3837     for (x = 1; x < num_clips; x++) {\r
3838       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3839         abort();   // this should never ever happen!\r
3840     } \r
3841   }\r
3842 \r
3843   /* Copy the new bitmap onto the screen in one go.\r
3844    * This way we avoid any flickering\r
3845    */\r
3846   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3847   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3848          boardRect.right - boardRect.left,\r
3849          boardRect.bottom - boardRect.top,\r
3850          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3851   if(saveDiagFlag) { \r
3852     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
3853     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3854 \r
3855     GetObject(bufferBitmap, sizeof(b), &b);\r
3856     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
3857         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3858         bih.biWidth = b.bmWidth;\r
3859         bih.biHeight = b.bmHeight;\r
3860         bih.biPlanes = 1;\r
3861         bih.biBitCount = b.bmBitsPixel;\r
3862         bih.biCompression = 0;\r
3863         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3864         bih.biXPelsPerMeter = 0;\r
3865         bih.biYPelsPerMeter = 0;\r
3866         bih.biClrUsed = 0;\r
3867         bih.biClrImportant = 0;\r
3868 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3869 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3870         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3871 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3872 \r
3873         wb = b.bmWidthBytes;\r
3874         // count colors\r
3875         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3876                 int k = ((int*) pData)[i];\r
3877                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3878                 if(j >= 16) break;\r
3879                 color[j] = k;\r
3880                 if(j >= nrColors) nrColors = j+1;\r
3881         }\r
3882         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3883                 INT p = 0;\r
3884                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3885                     for(w=0; w<(wb>>2); w+=2) {\r
3886                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3887                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3888                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3889                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3890                         pData[p++] = m | j<<4;\r
3891                     }\r
3892                     while(p&3) pData[p++] = 0;\r
3893                 }\r
3894                 fac = 3;\r
3895                 wb = ((wb+31)>>5)<<2;\r
3896         }\r
3897         // write BITMAPFILEHEADER\r
3898         fprintf(diagFile, "BM");\r
3899         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3900         fputDW(diagFile, 0);\r
3901         fputDW(diagFile, 0x36 + (fac?64:0));\r
3902         // write BITMAPINFOHEADER\r
3903         fputDW(diagFile, 40);\r
3904         fputDW(diagFile, b.bmWidth);\r
3905         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3906         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3907         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3908         fputDW(diagFile, 0);\r
3909         fputDW(diagFile, 0);\r
3910         fputDW(diagFile, 0);\r
3911         fputDW(diagFile, 0);\r
3912         fputDW(diagFile, 0);\r
3913         fputDW(diagFile, 0);\r
3914         // write color table\r
3915         if(fac)\r
3916         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3917         // write bitmap data\r
3918         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3919                 fputc(pData[i], diagFile);\r
3920         free(pData);\r
3921      }\r
3922   }\r
3923 \r
3924   SelectObject(tmphdc, oldBitmap);\r
3925 \r
3926   /* Massive cleanup */\r
3927   for (x = 0; x < num_clips; x++)\r
3928     DeleteObject(clips[x]);\r
3929 \r
3930   DeleteDC(tmphdc);\r
3931   DeleteObject(bufferBitmap);\r
3932 \r
3933   if (releaseDC) \r
3934     ReleaseDC(hwndMain, hdc);\r
3935   \r
3936   if (lastDrawnFlipView != flipView && nr == 0) {\r
3937     if (flipView)\r
3938       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3939     else\r
3940       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3941   }\r
3942 \r
3943 /*  CopyBoard(lastDrawn, board);*/\r
3944   lastDrawnHighlight = highlightInfo;\r
3945   lastDrawnPremove   = premoveHighlightInfo;\r
3946   lastDrawnFlipView = flipView;\r
3947   lastDrawnValid[nr] = 1;\r
3948 }\r
3949 \r
3950 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3951 int\r
3952 SaveDiagram(f)\r
3953      FILE *f;\r
3954 {\r
3955     saveDiagFlag = 1; diagFile = f;\r
3956     HDCDrawPosition(NULL, TRUE, NULL);\r
3957     saveDiagFlag = 0;\r
3958 \r
3959     fclose(f);\r
3960     return TRUE;\r
3961 }\r
3962 \r
3963 \r
3964 /*---------------------------------------------------------------------------*\\r
3965 | CLIENT PAINT PROCEDURE\r
3966 |   This is the main event-handler for the WM_PAINT message.\r
3967 |\r
3968 \*---------------------------------------------------------------------------*/\r
3969 VOID\r
3970 PaintProc(HWND hwnd)\r
3971 {\r
3972   HDC         hdc;\r
3973   PAINTSTRUCT ps;\r
3974   HFONT       oldFont;\r
3975 \r
3976   if((hdc = BeginPaint(hwnd, &ps))) {\r
3977     if (IsIconic(hwnd)) {\r
3978       DrawIcon(hdc, 2, 2, iconCurrent);\r
3979     } else {\r
3980       if (!appData.monoMode) {\r
3981         SelectPalette(hdc, hPal, FALSE);\r
3982         RealizePalette(hdc);\r
3983       }\r
3984       HDCDrawPosition(hdc, 1, NULL);\r
3985       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
3986         flipView = !flipView; partnerUp = !partnerUp;\r
3987         HDCDrawPosition(hdc, 1, NULL);\r
3988         flipView = !flipView; partnerUp = !partnerUp;\r
3989       }\r
3990       oldFont =\r
3991         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3992       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
3993                  ETO_CLIPPED|ETO_OPAQUE,\r
3994                  &messageRect, messageText, strlen(messageText), NULL);\r
3995       SelectObject(hdc, oldFont);\r
3996       DisplayBothClocks();\r
3997       DisplayLogos();\r
3998     }\r
3999     EndPaint(hwnd,&ps);\r
4000   }\r
4001 \r
4002   return;\r
4003 }\r
4004 \r
4005 \r
4006 /*\r
4007  * If the user selects on a border boundary, return -1; if off the board,\r
4008  *   return -2.  Otherwise map the event coordinate to the square.\r
4009  * The offset boardRect.left or boardRect.top must already have been\r
4010  *   subtracted from x.\r
4011  */\r
4012 int EventToSquare(x, limit)\r
4013      int x, limit;\r
4014 {\r
4015   if (x <= 0)\r
4016     return -2;\r
4017   if (x < lineGap)\r
4018     return -1;\r
4019   x -= lineGap;\r
4020   if ((x % (squareSize + lineGap)) >= squareSize)\r
4021     return -1;\r
4022   x /= (squareSize + lineGap);\r
4023     if (x >= limit)\r
4024     return -2;\r
4025   return x;\r
4026 }\r
4027 \r
4028 typedef struct {\r
4029   char piece;\r
4030   int command;\r
4031   char* name;\r
4032 } DropEnable;\r
4033 \r
4034 DropEnable dropEnables[] = {\r
4035   { 'P', DP_Pawn, N_("Pawn") },\r
4036   { 'N', DP_Knight, N_("Knight") },\r
4037   { 'B', DP_Bishop, N_("Bishop") },\r
4038   { 'R', DP_Rook, N_("Rook") },\r
4039   { 'Q', DP_Queen, N_("Queen") },\r
4040 };\r
4041 \r
4042 VOID\r
4043 SetupDropMenu(HMENU hmenu)\r
4044 {\r
4045   int i, count, enable;\r
4046   char *p;\r
4047   extern char white_holding[], black_holding[];\r
4048   char item[MSG_SIZ];\r
4049 \r
4050   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4051     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4052                dropEnables[i].piece);\r
4053     count = 0;\r
4054     while (p && *p++ == dropEnables[i].piece) count++;\r
4055       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4056     enable = count > 0 || !appData.testLegality\r
4057       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4058                       && !appData.icsActive);\r
4059     ModifyMenu(hmenu, dropEnables[i].command,\r
4060                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4061                dropEnables[i].command, item);\r
4062   }\r
4063 }\r
4064 \r
4065 void DragPieceBegin(int x, int y)\r
4066 {\r
4067       dragInfo.lastpos.x = boardRect.left + x;\r
4068       dragInfo.lastpos.y = boardRect.top + y;\r
4069       dragInfo.from.x = fromX;\r
4070       dragInfo.from.y = fromY;\r
4071       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4072       dragInfo.start = dragInfo.from;\r
4073       SetCapture(hwndMain);\r
4074 }\r
4075 \r
4076 void DragPieceEnd(int x, int y)\r
4077 {\r
4078     ReleaseCapture();\r
4079     dragInfo.start.x = dragInfo.start.y = -1;\r
4080     dragInfo.from = dragInfo.start;\r
4081     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4082 }\r
4083 \r
4084 void ChangeDragPiece(ChessSquare piece)\r
4085 {\r
4086     dragInfo.piece = piece;\r
4087 }\r
4088 \r
4089 /* Event handler for mouse messages */\r
4090 VOID\r
4091 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4092 {\r
4093   int x, y, menuNr;\r
4094   POINT pt;\r
4095   static int recursive = 0;\r
4096   HMENU hmenu;\r
4097   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4098 \r
4099   if (recursive) {\r
4100     if (message == WM_MBUTTONUP) {\r
4101       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4102          to the middle button: we simulate pressing the left button too!\r
4103          */\r
4104       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4105       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4106     }\r
4107     return;\r
4108   }\r
4109   recursive++;\r
4110   \r
4111   pt.x = LOWORD(lParam);\r
4112   pt.y = HIWORD(lParam);\r
4113   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4114   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4115   if (!flipView && y >= 0) {\r
4116     y = BOARD_HEIGHT - 1 - y;\r
4117   }\r
4118   if (flipView && x >= 0) {\r
4119     x = BOARD_WIDTH - 1 - x;\r
4120   }\r
4121 \r
4122   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4123 \r
4124   switch (message) {\r
4125   case WM_LBUTTONDOWN:\r
4126       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4127         ClockClick(flipClock);\r
4128       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4129         ClockClick(!flipClock);\r
4130       }\r
4131       dragInfo.start.x = dragInfo.start.y = -1;\r
4132       dragInfo.from = dragInfo.start;\r
4133     if(fromX == -1 && frozen) { // not sure where this is for\r
4134                 fromX = fromY = -1; \r
4135       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4136       break;\r
4137     }\r
4138       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4139       DrawPosition(TRUE, NULL);\r
4140     break;\r
4141 \r
4142   case WM_LBUTTONUP:\r
4143       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4144       DrawPosition(TRUE, NULL);\r
4145     break;\r
4146 \r
4147   case WM_MOUSEMOVE:\r
4148     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4149     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4150     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4151     if ((appData.animateDragging || appData.highlightDragging)\r
4152         && (wParam & MK_LBUTTON)\r
4153         && dragInfo.from.x >= 0) \r
4154     {\r
4155       BOOL full_repaint = FALSE;\r
4156 \r
4157       if (appData.animateDragging) {\r
4158         dragInfo.pos = pt;\r
4159       }\r
4160       if (appData.highlightDragging) {\r
4161         SetHighlights(fromX, fromY, x, y);\r
4162         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4163             full_repaint = TRUE;\r
4164         }\r
4165       }\r
4166       \r
4167       DrawPosition( full_repaint, NULL);\r
4168       \r
4169       dragInfo.lastpos = dragInfo.pos;\r
4170     }\r
4171     break;\r
4172 \r
4173   case WM_MOUSEWHEEL: // [DM]\r
4174     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4175        /* Mouse Wheel is being rolled forward\r
4176         * Play moves forward\r
4177         */\r
4178        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4179                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4180        /* Mouse Wheel is being rolled backward\r
4181         * Play moves backward\r
4182         */\r
4183        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4184                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4185     }\r
4186     break;\r
4187 \r
4188   case WM_MBUTTONUP:\r
4189   case WM_RBUTTONUP:\r
4190     ReleaseCapture();\r
4191     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4192     break;\r
4193  \r
4194   case WM_MBUTTONDOWN:\r
4195   case WM_RBUTTONDOWN:\r
4196     ErrorPopDown();\r
4197     ReleaseCapture();\r
4198     fromX = fromY = -1;\r
4199     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4200     dragInfo.start.x = dragInfo.start.y = -1;\r
4201     dragInfo.from = dragInfo.start;\r
4202     dragInfo.lastpos = dragInfo.pos;\r
4203     if (appData.highlightDragging) {\r
4204       ClearHighlights();\r
4205     }\r
4206     if(y == -2) {\r
4207       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4208       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4209           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4210       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4211           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4212       }\r
4213       break;\r
4214     }\r
4215     DrawPosition(TRUE, NULL);\r
4216 \r
4217     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4218     switch (menuNr) {\r
4219     case 0:\r
4220       if (message == WM_MBUTTONDOWN) {\r
4221         buttonCount = 3;  /* even if system didn't think so */\r
4222         if (wParam & MK_SHIFT) \r
4223           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4224         else\r
4225           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4226       } else { /* message == WM_RBUTTONDOWN */\r
4227         /* Just have one menu, on the right button.  Windows users don't\r
4228            think to try the middle one, and sometimes other software steals\r
4229            it, or it doesn't really exist. */\r
4230         if(gameInfo.variant != VariantShogi)\r
4231             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4232         else\r
4233             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4234       }\r
4235       break;\r
4236     case 2:\r
4237       SetCapture(hwndMain);
4238       break;\r
4239     case 1:\r
4240       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4241       SetupDropMenu(hmenu);\r
4242       MenuPopup(hwnd, pt, hmenu, -1);\r
4243     default:\r
4244       break;\r
4245     }\r
4246     break;\r
4247   }\r
4248 \r
4249   recursive--;\r
4250 }\r
4251 \r
4252 /* Preprocess messages for buttons in main window */\r
4253 LRESULT CALLBACK\r
4254 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4255 {\r
4256   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4257   int i, dir;\r
4258 \r
4259   for (i=0; i<N_BUTTONS; i++) {\r
4260     if (buttonDesc[i].id == id) break;\r
4261   }\r
4262   if (i == N_BUTTONS) return 0;\r
4263   switch (message) {\r
4264   case WM_KEYDOWN:\r
4265     switch (wParam) {\r
4266     case VK_LEFT:\r
4267     case VK_RIGHT:\r
4268       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4269       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4270       return TRUE;\r
4271     }\r
4272     break;\r
4273   case WM_CHAR:\r
4274     switch (wParam) {\r
4275     case '\r':\r
4276       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4277       return TRUE;\r
4278     default:\r
4279       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4280         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4281         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4282         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4283         SetFocus(h);\r
4284         SendMessage(h, WM_CHAR, wParam, lParam);\r
4285         return TRUE;\r
4286       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4287         TypeInEvent((char)wParam);\r
4288       }\r
4289       break;\r
4290     }\r
4291     break;\r
4292   }\r
4293   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4294 }\r
4295 \r
4296 /* Process messages for Promotion dialog box */\r
4297 LRESULT CALLBACK\r
4298 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4299 {\r
4300   char promoChar;\r
4301 \r
4302   switch (message) {\r
4303   case WM_INITDIALOG: /* message: initialize dialog box */\r
4304     /* Center the dialog over the application window */\r
4305     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4306     Translate(hDlg, DLG_PromotionKing);\r
4307     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4308       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4309        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4310        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4311                SW_SHOW : SW_HIDE);\r
4312     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4313     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4314        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4315          PieceToChar(WhiteAngel) != '~') ||\r
4316         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4317          PieceToChar(BlackAngel) != '~')   ) ?\r
4318                SW_SHOW : SW_HIDE);\r
4319     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4320        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4321          PieceToChar(WhiteMarshall) != '~') ||\r
4322         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4323          PieceToChar(BlackMarshall) != '~')   ) ?\r
4324                SW_SHOW : SW_HIDE);\r
4325     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4326     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4327        gameInfo.variant != VariantShogi ?\r
4328                SW_SHOW : SW_HIDE);\r
4329     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4330        gameInfo.variant != VariantShogi ?\r
4331                SW_SHOW : SW_HIDE);\r
4332     if(gameInfo.variant == VariantShogi) {\r
4333         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4334         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4335         SetWindowText(hDlg, "Promote?");\r
4336     }\r
4337     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4338        gameInfo.variant == VariantSuper ?\r
4339                SW_SHOW : SW_HIDE);\r
4340     return TRUE;\r
4341 \r
4342   case WM_COMMAND: /* message: received a command */\r
4343     switch (LOWORD(wParam)) {\r
4344     case IDCANCEL:\r
4345       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4346       ClearHighlights();\r
4347       DrawPosition(FALSE, NULL);\r
4348       return TRUE;\r
4349     case PB_King:\r
4350       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4351       break;\r
4352     case PB_Queen:\r
4353       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4354       break;\r
4355     case PB_Rook:\r
4356       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4357       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4358       break;\r
4359     case PB_Bishop:\r
4360       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4361       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4362       break;\r
4363     case PB_Chancellor:\r
4364       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4365       break;\r
4366     case PB_Archbishop:\r
4367       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4368       break;\r
4369     case PB_Knight:\r
4370       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4371       break;\r
4372     default:\r
4373       return FALSE;\r
4374     }\r
4375     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4376     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4377     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4378     fromX = fromY = -1;\r
4379     if (!appData.highlightLastMove) {\r
4380       ClearHighlights();\r
4381       DrawPosition(FALSE, NULL);\r
4382     }\r
4383     return TRUE;\r
4384   }\r
4385   return FALSE;\r
4386 }\r
4387 \r
4388 /* Pop up promotion dialog */\r
4389 VOID\r
4390 PromotionPopup(HWND hwnd)\r
4391 {\r
4392   FARPROC lpProc;\r
4393 \r
4394   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4395   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4396     hwnd, (DLGPROC)lpProc);\r
4397   FreeProcInstance(lpProc);\r
4398 }\r
4399 \r
4400 void\r
4401 PromotionPopUp()\r
4402 {\r
4403   DrawPosition(TRUE, NULL);\r
4404   PromotionPopup(hwndMain);\r
4405 }\r
4406 \r
4407 /* Toggle ShowThinking */\r
4408 VOID\r
4409 ToggleShowThinking()\r
4410 {\r
4411   appData.showThinking = !appData.showThinking;\r
4412   ShowThinkingEvent();\r
4413 }\r
4414 \r
4415 VOID\r
4416 LoadGameDialog(HWND hwnd, char* title)\r
4417 {\r
4418   UINT number = 0;\r
4419   FILE *f;\r
4420   char fileTitle[MSG_SIZ];\r
4421   f = OpenFileDialog(hwnd, "rb", "",\r
4422                      appData.oldSaveStyle ? "gam" : "pgn",\r
4423                      GAME_FILT,\r
4424                      title, &number, fileTitle, NULL);\r
4425   if (f != NULL) {\r
4426     cmailMsgLoaded = FALSE;\r
4427     if (number == 0) {\r
4428       int error = GameListBuild(f);\r
4429       if (error) {\r
4430         DisplayError(_("Cannot build game list"), error);\r
4431       } else if (!ListEmpty(&gameList) &&\r
4432                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4433         GameListPopUp(f, fileTitle);\r
4434         return;\r
4435       }\r
4436       GameListDestroy();\r
4437       number = 1;\r
4438     }\r
4439     LoadGame(f, number, fileTitle, FALSE);\r
4440   }\r
4441 }\r
4442 \r
4443 int get_term_width()\r
4444 {\r
4445     HDC hdc;\r
4446     TEXTMETRIC tm;\r
4447     RECT rc;\r
4448     HFONT hfont, hold_font;\r
4449     LOGFONT lf;\r
4450     HWND hText;\r
4451 \r
4452     if (hwndConsole)\r
4453         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4454     else\r
4455         return 79;\r
4456 \r
4457     // get the text metrics\r
4458     hdc = GetDC(hText);\r
4459     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4460     if (consoleCF.dwEffects & CFE_BOLD)\r
4461         lf.lfWeight = FW_BOLD;\r
4462     if (consoleCF.dwEffects & CFE_ITALIC)\r
4463         lf.lfItalic = TRUE;\r
4464     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4465         lf.lfStrikeOut = TRUE;\r
4466     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4467         lf.lfUnderline = TRUE;\r
4468     hfont = CreateFontIndirect(&lf);\r
4469     hold_font = SelectObject(hdc, hfont);\r
4470     GetTextMetrics(hdc, &tm);\r
4471     SelectObject(hdc, hold_font);\r
4472     DeleteObject(hfont);\r
4473     ReleaseDC(hText, hdc);\r
4474 \r
4475     // get the rectangle\r
4476     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4477 \r
4478     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4479 }\r
4480 \r
4481 void UpdateICSWidth(HWND hText)\r
4482 {\r
4483     LONG old_width, new_width;\r
4484 \r
4485     new_width = get_term_width(hText, FALSE);\r
4486     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4487     if (new_width != old_width)\r
4488     {\r
4489         ics_update_width(new_width);\r
4490         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4491     }\r
4492 }\r
4493 \r
4494 VOID\r
4495 ChangedConsoleFont()\r
4496 {\r
4497   CHARFORMAT cfmt;\r
4498   CHARRANGE tmpsel, sel;\r
4499   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4500   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4501   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4502   PARAFORMAT paraf;\r
4503 \r
4504   cfmt.cbSize = sizeof(CHARFORMAT);\r
4505   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4506     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4507                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4508   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4509    * size.  This was undocumented in the version of MSVC++ that I had\r
4510    * when I wrote the code, but is apparently documented now.\r
4511    */\r
4512   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4513   cfmt.bCharSet = f->lf.lfCharSet;\r
4514   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4515   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4516   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4517   /* Why are the following seemingly needed too? */\r
4518   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4519   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4520   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4521   tmpsel.cpMin = 0;\r
4522   tmpsel.cpMax = -1; /*999999?*/\r
4523   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4524   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4525   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4526    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4527    */\r
4528   paraf.cbSize = sizeof(paraf);\r
4529   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4530   paraf.dxStartIndent = 0;\r
4531   paraf.dxOffset = WRAP_INDENT;\r
4532   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4533   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4534   UpdateICSWidth(hText);\r
4535 }\r
4536 \r
4537 /*---------------------------------------------------------------------------*\\r
4538  *\r
4539  * Window Proc for main window\r
4540  *\r
4541 \*---------------------------------------------------------------------------*/\r
4542 \r
4543 /* Process messages for main window, etc. */\r
4544 LRESULT CALLBACK\r
4545 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4546 {\r
4547   FARPROC lpProc;\r
4548   int wmId, wmEvent;\r
4549   char *defName;\r
4550   FILE *f;\r
4551   UINT number;\r
4552   char fileTitle[MSG_SIZ];\r
4553   char buf[MSG_SIZ];\r
4554   static SnapData sd;\r
4555 \r
4556   switch (message) {\r
4557 \r
4558   case WM_PAINT: /* message: repaint portion of window */\r
4559     PaintProc(hwnd);\r
4560     break;\r
4561 \r
4562   case WM_ERASEBKGND:\r
4563     if (IsIconic(hwnd)) {\r
4564       /* Cheat; change the message */\r
4565       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4566     } else {\r
4567       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4568     }\r
4569     break;\r
4570 \r
4571   case WM_LBUTTONDOWN:\r
4572   case WM_MBUTTONDOWN:\r
4573   case WM_RBUTTONDOWN:\r
4574   case WM_LBUTTONUP:\r
4575   case WM_MBUTTONUP:\r
4576   case WM_RBUTTONUP:\r
4577   case WM_MOUSEMOVE:\r
4578   case WM_MOUSEWHEEL:\r
4579     MouseEvent(hwnd, message, wParam, lParam);\r
4580     break;\r
4581 \r
4582   JAWS_KB_NAVIGATION\r
4583 \r
4584   case WM_CHAR:\r
4585     \r
4586     JAWS_ALT_INTERCEPT\r
4587 \r
4588     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4589         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4590         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4591         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4592         SetFocus(h);\r
4593         SendMessage(h, message, wParam, lParam);\r
4594     } else if(lParam != KF_REPEAT) {\r
4595         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4596                 TypeInEvent((char)wParam);\r
4597         } else if((char)wParam == 003) CopyGameToClipboard();\r
4598          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4599     }\r
4600 \r
4601     break;\r
4602 \r
4603   case WM_PALETTECHANGED:\r
4604     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4605       int nnew;\r
4606       HDC hdc = GetDC(hwndMain);\r
4607       SelectPalette(hdc, hPal, TRUE);\r
4608       nnew = RealizePalette(hdc);\r
4609       if (nnew > 0) {\r
4610         paletteChanged = TRUE;\r
4611         InvalidateRect(hwnd, &boardRect, FALSE);\r
4612       }\r
4613       ReleaseDC(hwnd, hdc);\r
4614     }\r
4615     break;\r
4616 \r
4617   case WM_QUERYNEWPALETTE:\r
4618     if (!appData.monoMode /*&& paletteChanged*/) {\r
4619       int nnew;\r
4620       HDC hdc = GetDC(hwndMain);\r
4621       paletteChanged = FALSE;\r
4622       SelectPalette(hdc, hPal, FALSE);\r
4623       nnew = RealizePalette(hdc);\r
4624       if (nnew > 0) {\r
4625         InvalidateRect(hwnd, &boardRect, FALSE);\r
4626       }\r
4627       ReleaseDC(hwnd, hdc);\r
4628       return TRUE;\r
4629     }\r
4630     return FALSE;\r
4631 \r
4632   case WM_COMMAND: /* message: command from application menu */\r
4633     wmId    = LOWORD(wParam);\r
4634     wmEvent = HIWORD(wParam);\r
4635 \r
4636     switch (wmId) {\r
4637     case IDM_NewGame:\r
4638       ResetGameEvent();\r
4639       SAY("new game enter a move to play against the computer with white");\r
4640       break;\r
4641 \r
4642     case IDM_NewGameFRC:\r
4643       if( NewGameFRC() == 0 ) {\r
4644         ResetGameEvent();\r
4645       }\r
4646       break;\r
4647 \r
4648     case IDM_NewVariant:\r
4649       NewVariantPopup(hwnd);\r
4650       break;\r
4651 \r
4652     case IDM_LoadGame:\r
4653       LoadGameDialog(hwnd, _("Load Game from File"));\r
4654       break;\r
4655 \r
4656     case IDM_LoadNextGame:\r
4657       ReloadGame(1);\r
4658       break;\r
4659 \r
4660     case IDM_LoadPrevGame:\r
4661       ReloadGame(-1);\r
4662       break;\r
4663 \r
4664     case IDM_ReloadGame:\r
4665       ReloadGame(0);\r
4666       break;\r
4667 \r
4668     case IDM_LoadPosition:\r
4669       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4670         Reset(FALSE, TRUE);\r
4671       }\r
4672       number = 1;\r
4673       f = OpenFileDialog(hwnd, "rb", "",\r
4674                          appData.oldSaveStyle ? "pos" : "fen",\r
4675                          POSITION_FILT,\r
4676                          _("Load Position from File"), &number, fileTitle, NULL);\r
4677       if (f != NULL) {\r
4678         LoadPosition(f, number, fileTitle);\r
4679       }\r
4680       break;\r
4681 \r
4682     case IDM_LoadNextPosition:\r
4683       ReloadPosition(1);\r
4684       break;\r
4685 \r
4686     case IDM_LoadPrevPosition:\r
4687       ReloadPosition(-1);\r
4688       break;\r
4689 \r
4690     case IDM_ReloadPosition:\r
4691       ReloadPosition(0);\r
4692       break;\r
4693 \r
4694     case IDM_SaveGame:\r
4695       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4696       f = OpenFileDialog(hwnd, "a", defName,\r
4697                          appData.oldSaveStyle ? "gam" : "pgn",\r
4698                          GAME_FILT,\r
4699                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4700       if (f != NULL) {\r
4701         SaveGame(f, 0, "");\r
4702       }\r
4703       break;\r
4704 \r
4705     case IDM_SavePosition:\r
4706       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4707       f = OpenFileDialog(hwnd, "a", defName,\r
4708                          appData.oldSaveStyle ? "pos" : "fen",\r
4709                          POSITION_FILT,\r
4710                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4711       if (f != NULL) {\r
4712         SavePosition(f, 0, "");\r
4713       }\r
4714       break;\r
4715 \r
4716     case IDM_SaveDiagram:\r
4717       defName = "diagram";\r
4718       f = OpenFileDialog(hwnd, "wb", defName,\r
4719                          "bmp",\r
4720                          DIAGRAM_FILT,\r
4721                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4722       if (f != NULL) {\r
4723         SaveDiagram(f);\r
4724       }\r
4725       break;\r
4726 \r
4727     case IDM_CopyGame:\r
4728       CopyGameToClipboard();\r
4729       break;\r
4730 \r
4731     case IDM_PasteGame:\r
4732       PasteGameFromClipboard();\r
4733       break;\r
4734 \r
4735     case IDM_CopyGameListToClipboard:\r
4736       CopyGameListToClipboard();\r
4737       break;\r
4738 \r
4739     /* [AS] Autodetect FEN or PGN data */\r
4740     case IDM_PasteAny:\r
4741       PasteGameOrFENFromClipboard();\r
4742       break;\r
4743 \r
4744     /* [AS] Move history */\r
4745     case IDM_ShowMoveHistory:\r
4746         if( MoveHistoryIsUp() ) {\r
4747             MoveHistoryPopDown();\r
4748         }\r
4749         else {\r
4750             MoveHistoryPopUp();\r
4751         }\r
4752         break;\r
4753 \r
4754     /* [AS] Eval graph */\r
4755     case IDM_ShowEvalGraph:\r
4756         if( EvalGraphIsUp() ) {\r
4757             EvalGraphPopDown();\r
4758         }\r
4759         else {\r
4760             EvalGraphPopUp();\r
4761             SetFocus(hwndMain);\r
4762         }\r
4763         break;\r
4764 \r
4765     /* [AS] Engine output */\r
4766     case IDM_ShowEngineOutput:\r
4767         if( EngineOutputIsUp() ) {\r
4768             EngineOutputPopDown();\r
4769         }\r
4770         else {\r
4771             EngineOutputPopUp();\r
4772         }\r
4773         break;\r
4774 \r
4775     /* [AS] User adjudication */\r
4776     case IDM_UserAdjudication_White:\r
4777         UserAdjudicationEvent( +1 );\r
4778         break;\r
4779 \r
4780     case IDM_UserAdjudication_Black:\r
4781         UserAdjudicationEvent( -1 );\r
4782         break;\r
4783 \r
4784     case IDM_UserAdjudication_Draw:\r
4785         UserAdjudicationEvent( 0 );\r
4786         break;\r
4787 \r
4788     /* [AS] Game list options dialog */\r
4789     case IDM_GameListOptions:\r
4790       GameListOptions();\r
4791       break;\r
4792 \r
4793     case IDM_NewChat:\r
4794       ChatPopUp(NULL);\r
4795       break;\r
4796 \r
4797     case IDM_CopyPosition:\r
4798       CopyFENToClipboard();\r
4799       break;\r
4800 \r
4801     case IDM_PastePosition:\r
4802       PasteFENFromClipboard();\r
4803       break;\r
4804 \r
4805     case IDM_MailMove:\r
4806       MailMoveEvent();\r
4807       break;\r
4808 \r
4809     case IDM_ReloadCMailMsg:\r
4810       Reset(TRUE, TRUE);\r
4811       ReloadCmailMsgEvent(FALSE);\r
4812       break;\r
4813 \r
4814     case IDM_Minimize:\r
4815       ShowWindow(hwnd, SW_MINIMIZE);\r
4816       break;\r
4817 \r
4818     case IDM_Exit:\r
4819       ExitEvent(0);\r
4820       break;\r
4821 \r
4822     case IDM_MachineWhite:\r
4823       MachineWhiteEvent();\r
4824       /*\r
4825        * refresh the tags dialog only if it's visible\r
4826        */\r
4827       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4828           char *tags;\r
4829           tags = PGNTags(&gameInfo);\r
4830           TagsPopUp(tags, CmailMsg());\r
4831           free(tags);\r
4832       }\r
4833       SAY("computer starts playing white");\r
4834       break;\r
4835 \r
4836     case IDM_MachineBlack:\r
4837       MachineBlackEvent();\r
4838       /*\r
4839        * refresh the tags dialog only if it's visible\r
4840        */\r
4841       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4842           char *tags;\r
4843           tags = PGNTags(&gameInfo);\r
4844           TagsPopUp(tags, CmailMsg());\r
4845           free(tags);\r
4846       }\r
4847       SAY("computer starts playing black");\r
4848       break;\r
4849 \r
4850     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4851       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
4852       break;\r
4853 \r
4854     case IDM_TwoMachines:\r
4855       TwoMachinesEvent();\r
4856       /*\r
4857        * refresh the tags dialog only if it's visible\r
4858        */\r
4859       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4860           char *tags;\r
4861           tags = PGNTags(&gameInfo);\r
4862           TagsPopUp(tags, CmailMsg());\r
4863           free(tags);\r
4864       }\r
4865       SAY("computer starts playing both sides");\r
4866       break;\r
4867 \r
4868     case IDM_AnalysisMode:\r
4869       if (!first.analysisSupport) {\r
4870         snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4871         DisplayError(buf, 0);\r
4872       } else {\r
4873         SAY("analyzing current position");\r
4874         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4875         if (appData.icsActive) {\r
4876                if (gameMode != IcsObserving) {\r
4877                  snprintf(buf, MSG_SIZ, "You are not observing a game");\r
4878                        DisplayError(buf, 0);\r
4879                        /* secure check */\r
4880                        if (appData.icsEngineAnalyze) {\r
4881                                if (appData.debugMode) \r
4882                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4883                                ExitAnalyzeMode();\r
4884                                ModeHighlight();\r
4885                                break;\r
4886                        }\r
4887                        break;\r
4888                } else {\r
4889                        /* if enable, user want disable icsEngineAnalyze */\r
4890                        if (appData.icsEngineAnalyze) {\r
4891                                ExitAnalyzeMode();\r
4892                                ModeHighlight();\r
4893                                break;\r
4894                        }\r
4895                        appData.icsEngineAnalyze = TRUE;\r
4896                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4897                }\r
4898         } \r
4899         if (!appData.showThinking) ToggleShowThinking();\r
4900         AnalyzeModeEvent();\r
4901       }\r
4902       break;\r
4903 \r
4904     case IDM_AnalyzeFile:\r
4905       if (!first.analysisSupport) {\r
4906         char buf[MSG_SIZ];\r
4907           snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4908         DisplayError(buf, 0);\r
4909       } else {\r
4910         if (!appData.showThinking) ToggleShowThinking();\r
4911         AnalyzeFileEvent();\r
4912         LoadGameDialog(hwnd, _("Analyze Game from File"));\r
4913         AnalysisPeriodicEvent(1);\r
4914       }\r
4915       break;\r
4916 \r
4917     case IDM_IcsClient:\r
4918       IcsClientEvent();\r
4919       break;\r
4920 \r
4921     case IDM_EditGame:\r
4922     case IDM_EditGame2:\r
4923       EditGameEvent();\r
4924       SAY("edit game");\r
4925       break;\r
4926 \r
4927     case IDM_EditPosition:\r
4928     case IDM_EditPosition2:\r
4929       EditPositionEvent();\r
4930       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4931       break;\r
4932 \r
4933     case IDM_Training:\r
4934       TrainingEvent();\r
4935       break;\r
4936 \r
4937     case IDM_ShowGameList:\r
4938       ShowGameListProc();\r
4939       break;\r
4940 \r
4941     case IDM_EditProgs1:\r
4942       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
4943       break;\r
4944 \r
4945     case IDM_EditProgs2:\r
4946      LoadEnginePopUp(hwndMain);\r
4947 //      EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);\r
4948       break;\r
4949 \r
4950     case IDM_EditServers:\r
4951       EditTagsPopUp(icsNames, &icsNames);\r
4952       break;\r
4953 \r
4954     case IDM_EditTags:\r
4955     case IDM_Tags:\r
4956       EditTagsProc();\r
4957       break;\r
4958 \r
4959     case IDM_EditComment:\r
4960     case IDM_Comment:\r
4961       if (commentUp && editComment) {\r
4962         CommentPopDown();\r
4963       } else {\r
4964         EditCommentEvent();\r
4965       }\r
4966       break;\r
4967 \r
4968     case IDM_Pause:\r
4969       PauseEvent();\r
4970       break;\r
4971 \r
4972     case IDM_Accept:\r
4973       AcceptEvent();\r
4974       break;\r
4975 \r
4976     case IDM_Decline:\r
4977       DeclineEvent();\r
4978       break;\r
4979 \r
4980     case IDM_Rematch:\r
4981       RematchEvent();\r
4982       break;\r
4983 \r
4984     case IDM_CallFlag:\r
4985       CallFlagEvent();\r
4986       break;\r
4987 \r
4988     case IDM_Draw:\r
4989       DrawEvent();\r
4990       break;\r
4991 \r
4992     case IDM_Adjourn:\r
4993       AdjournEvent();\r
4994       break;\r
4995 \r
4996     case IDM_Abort:\r
4997       AbortEvent();\r
4998       break;\r
4999 \r
5000     case IDM_Resign:\r
5001       ResignEvent();\r
5002       break;\r
5003 \r
5004     case IDM_StopObserving:\r
5005       StopObservingEvent();\r
5006       break;\r
5007 \r
5008     case IDM_StopExamining:\r
5009       StopExaminingEvent();\r
5010       break;\r
5011 \r
5012     case IDM_Upload:\r
5013       UploadGameEvent();\r
5014       break;\r
5015 \r
5016     case IDM_TypeInMove:\r
5017       TypeInEvent('\000');\r
5018       break;\r
5019 \r
5020     case IDM_TypeInName:\r
5021       PopUpNameDialog('\000');\r
5022       break;\r
5023 \r
5024     case IDM_Backward:\r
5025       BackwardEvent();\r
5026       SetFocus(hwndMain);\r
5027       break;\r
5028 \r
5029     JAWS_MENU_ITEMS\r
5030 \r
5031     case IDM_Forward:\r
5032       ForwardEvent();\r
5033       SetFocus(hwndMain);\r
5034       break;\r
5035 \r
5036     case IDM_ToStart:\r
5037       ToStartEvent();\r
5038       SetFocus(hwndMain);\r
5039       break;\r
5040 \r
5041     case IDM_ToEnd:\r
5042       ToEndEvent();\r
5043       SetFocus(hwndMain);\r
5044       break;\r
5045 \r
5046     case IDM_Revert:\r
5047       RevertEvent(FALSE);\r
5048       break;\r
5049 \r
5050     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5051       RevertEvent(TRUE);\r
5052       break;\r
5053 \r
5054     case IDM_TruncateGame:\r
5055       TruncateGameEvent();\r
5056       break;\r
5057 \r
5058     case IDM_MoveNow:\r
5059       MoveNowEvent();\r
5060       break;\r
5061 \r
5062     case IDM_RetractMove:\r
5063       RetractMoveEvent();\r
5064       break;\r
5065 \r
5066     case IDM_FlipView:\r
5067       flipView = !flipView;\r
5068       DrawPosition(FALSE, NULL);\r
5069       break;\r
5070 \r
5071     case IDM_FlipClock:\r
5072       flipClock = !flipClock;\r
5073       DisplayBothClocks();\r
5074       DisplayLogos();\r
5075       break;\r
5076 \r
5077     case IDM_MuteSounds:\r
5078       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5079       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5080                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5081       break;\r
5082 \r
5083     case IDM_GeneralOptions:\r
5084       GeneralOptionsPopup(hwnd);\r
5085       DrawPosition(TRUE, NULL);\r
5086       break;\r
5087 \r
5088     case IDM_BoardOptions:\r
5089       BoardOptionsPopup(hwnd);\r
5090       break;\r
5091 \r
5092     case IDM_EnginePlayOptions:\r
5093       EnginePlayOptionsPopup(hwnd);\r
5094       break;\r
5095 \r
5096     case IDM_Engine1Options:\r
5097       EngineOptionsPopup(hwnd, &first);\r
5098       break;\r
5099 \r
5100     case IDM_Engine2Options:\r
5101       savedHwnd = hwnd;\r
5102       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5103       EngineOptionsPopup(hwnd, &second);\r
5104       break;\r
5105 \r
5106     case IDM_OptionsUCI:\r
5107       UciOptionsPopup(hwnd);\r
5108       break;\r
5109 \r
5110     case IDM_Tourney:\r
5111       TourneyPopup(hwnd);\r
5112       break;\r
5113 \r
5114     case IDM_IcsOptions:\r
5115       IcsOptionsPopup(hwnd);\r
5116       break;\r
5117 \r
5118     case IDM_Fonts:\r
5119       FontsOptionsPopup(hwnd);\r
5120       break;\r
5121 \r
5122     case IDM_Sounds:\r
5123       SoundOptionsPopup(hwnd);\r
5124       break;\r
5125 \r
5126     case IDM_CommPort:\r
5127       CommPortOptionsPopup(hwnd);\r
5128       break;\r
5129 \r
5130     case IDM_LoadOptions:\r
5131       LoadOptionsPopup(hwnd);\r
5132       break;\r
5133 \r
5134     case IDM_SaveOptions:\r
5135       SaveOptionsPopup(hwnd);\r
5136       break;\r
5137 \r
5138     case IDM_TimeControl:\r
5139       TimeControlOptionsPopup(hwnd);\r
5140       break;\r
5141 \r
5142     case IDM_SaveSettings:\r
5143       SaveSettings(settingsFileName);\r
5144       break;\r
5145 \r
5146     case IDM_SaveSettingsOnExit:\r
5147       saveSettingsOnExit = !saveSettingsOnExit;\r
5148       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5149                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5150                                          MF_CHECKED : MF_UNCHECKED));\r
5151       break;\r
5152 \r
5153     case IDM_Hint:\r
5154       HintEvent();\r
5155       break;\r
5156 \r
5157     case IDM_Book:\r
5158       BookEvent();\r
5159       break;\r
5160 \r
5161     case IDM_AboutGame:\r
5162       AboutGameEvent();\r
5163       break;\r
5164 \r
5165     case IDM_Debug:\r
5166       appData.debugMode = !appData.debugMode;\r
5167       if (appData.debugMode) {\r
5168         char dir[MSG_SIZ];\r
5169         GetCurrentDirectory(MSG_SIZ, dir);\r
5170         SetCurrentDirectory(installDir);\r
5171         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5172         SetCurrentDirectory(dir);\r
5173         setbuf(debugFP, NULL);\r
5174       } else {\r
5175         fclose(debugFP);\r
5176         debugFP = NULL;\r
5177       }\r
5178       break;\r
5179 \r
5180     case IDM_HELPCONTENTS:\r
5181       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5182           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5183           MessageBox (GetFocus(),\r
5184                     _("Unable to activate help"),\r
5185                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5186       }\r
5187       break;\r
5188 \r
5189     case IDM_HELPSEARCH:\r
5190         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5191             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5192         MessageBox (GetFocus(),\r
5193                     _("Unable to activate help"),\r
5194                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5195       }\r
5196       break;\r
5197 \r
5198     case IDM_HELPHELP:\r
5199       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5200         MessageBox (GetFocus(),\r
5201                     _("Unable to activate help"),\r
5202                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5203       }\r
5204       break;\r
5205 \r
5206     case IDM_ABOUT:\r
5207       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5208       DialogBox(hInst, \r
5209         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5210         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5211       FreeProcInstance(lpProc);\r
5212       break;\r
5213 \r
5214     case IDM_DirectCommand1:\r
5215       AskQuestionEvent(_("Direct Command"),\r
5216                        _("Send to chess program:"), "", "1");\r
5217       break;\r
5218     case IDM_DirectCommand2:\r
5219       AskQuestionEvent(_("Direct Command"),\r
5220                        _("Send to second chess program:"), "", "2");\r
5221       break;\r
5222 \r
5223     case EP_WhitePawn:\r
5224       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5225       fromX = fromY = -1;\r
5226       break;\r
5227 \r
5228     case EP_WhiteKnight:\r
5229       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5230       fromX = fromY = -1;\r
5231       break;\r
5232 \r
5233     case EP_WhiteBishop:\r
5234       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5235       fromX = fromY = -1;\r
5236       break;\r
5237 \r
5238     case EP_WhiteRook:\r
5239       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5240       fromX = fromY = -1;\r
5241       break;\r
5242 \r
5243     case EP_WhiteQueen:\r
5244       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5245       fromX = fromY = -1;\r
5246       break;\r
5247 \r
5248     case EP_WhiteFerz:\r
5249       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5250       fromX = fromY = -1;\r
5251       break;\r
5252 \r
5253     case EP_WhiteWazir:\r
5254       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5255       fromX = fromY = -1;\r
5256       break;\r
5257 \r
5258     case EP_WhiteAlfil:\r
5259       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5260       fromX = fromY = -1;\r
5261       break;\r
5262 \r
5263     case EP_WhiteCannon:\r
5264       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5265       fromX = fromY = -1;\r
5266       break;\r
5267 \r
5268     case EP_WhiteCardinal:\r
5269       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5270       fromX = fromY = -1;\r
5271       break;\r
5272 \r
5273     case EP_WhiteMarshall:\r
5274       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5275       fromX = fromY = -1;\r
5276       break;\r
5277 \r
5278     case EP_WhiteKing:\r
5279       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5280       fromX = fromY = -1;\r
5281       break;\r
5282 \r
5283     case EP_BlackPawn:\r
5284       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5285       fromX = fromY = -1;\r
5286       break;\r
5287 \r
5288     case EP_BlackKnight:\r
5289       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5290       fromX = fromY = -1;\r
5291       break;\r
5292 \r
5293     case EP_BlackBishop:\r
5294       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5295       fromX = fromY = -1;\r
5296       break;\r
5297 \r
5298     case EP_BlackRook:\r
5299       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5300       fromX = fromY = -1;\r
5301       break;\r
5302 \r
5303     case EP_BlackQueen:\r
5304       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5305       fromX = fromY = -1;\r
5306       break;\r
5307 \r
5308     case EP_BlackFerz:\r
5309       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5310       fromX = fromY = -1;\r
5311       break;\r
5312 \r
5313     case EP_BlackWazir:\r
5314       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5315       fromX = fromY = -1;\r
5316       break;\r
5317 \r
5318     case EP_BlackAlfil:\r
5319       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5320       fromX = fromY = -1;\r
5321       break;\r
5322 \r
5323     case EP_BlackCannon:\r
5324       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5325       fromX = fromY = -1;\r
5326       break;\r
5327 \r
5328     case EP_BlackCardinal:\r
5329       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5330       fromX = fromY = -1;\r
5331       break;\r
5332 \r
5333     case EP_BlackMarshall:\r
5334       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5335       fromX = fromY = -1;\r
5336       break;\r
5337 \r
5338     case EP_BlackKing:\r
5339       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5340       fromX = fromY = -1;\r
5341       break;\r
5342 \r
5343     case EP_EmptySquare:\r
5344       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5345       fromX = fromY = -1;\r
5346       break;\r
5347 \r
5348     case EP_ClearBoard:\r
5349       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5350       fromX = fromY = -1;\r
5351       break;\r
5352 \r
5353     case EP_White:\r
5354       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5355       fromX = fromY = -1;\r
5356       break;\r
5357 \r
5358     case EP_Black:\r
5359       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5360       fromX = fromY = -1;\r
5361       break;\r
5362 \r
5363     case EP_Promote:\r
5364       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5365       fromX = fromY = -1;\r
5366       break;\r
5367 \r
5368     case EP_Demote:\r
5369       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5370       fromX = fromY = -1;\r
5371       break;\r
5372 \r
5373     case DP_Pawn:\r
5374       DropMenuEvent(WhitePawn, fromX, fromY);\r
5375       fromX = fromY = -1;\r
5376       break;\r
5377 \r
5378     case DP_Knight:\r
5379       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5380       fromX = fromY = -1;\r
5381       break;\r
5382 \r
5383     case DP_Bishop:\r
5384       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5385       fromX = fromY = -1;\r
5386       break;\r
5387 \r
5388     case DP_Rook:\r
5389       DropMenuEvent(WhiteRook, fromX, fromY);\r
5390       fromX = fromY = -1;\r
5391       break;\r
5392 \r
5393     case DP_Queen:\r
5394       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5395       fromX = fromY = -1;\r
5396       break;\r
5397 \r
5398     case IDM_English:\r
5399       barbaric = 0; appData.language = "";\r
5400       TranslateMenus(0);\r
5401       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5402       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5403       lastChecked = wmId;\r
5404       break;\r
5405 \r
5406     default:\r
5407       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5408           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5409           TranslateMenus(0);\r
5410           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5411           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5412           lastChecked = wmId;\r
5413           break;\r
5414       }\r
5415       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5416     }\r
5417     break;\r
5418 \r
5419   case WM_TIMER:\r
5420     switch (wParam) {\r
5421     case CLOCK_TIMER_ID:\r
5422       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5423       clockTimerEvent = 0;\r
5424       DecrementClocks(); /* call into back end */\r
5425       break;\r
5426     case LOAD_GAME_TIMER_ID:\r
5427       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5428       loadGameTimerEvent = 0;\r
5429       AutoPlayGameLoop(); /* call into back end */\r
5430       break;\r
5431     case ANALYSIS_TIMER_ID:\r
5432       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5433                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5434         AnalysisPeriodicEvent(0);\r
5435       } else {\r
5436         KillTimer(hwnd, analysisTimerEvent);\r
5437         analysisTimerEvent = 0;\r
5438       }\r
5439       break;\r
5440     case DELAYED_TIMER_ID:\r
5441       KillTimer(hwnd, delayedTimerEvent);\r
5442       delayedTimerEvent = 0;\r
5443       delayedTimerCallback();\r
5444       break;\r
5445     }\r
5446     break;\r
5447 \r
5448   case WM_USER_Input:\r
5449     InputEvent(hwnd, message, wParam, lParam);\r
5450     break;\r
5451 \r
5452   /* [AS] Also move "attached" child windows */\r
5453   case WM_WINDOWPOSCHANGING:\r
5454 \r
5455     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5456         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5457 \r
5458         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5459             /* Window is moving */\r
5460             RECT rcMain;\r
5461 \r
5462 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5463             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5464             rcMain.right  = wpMain.x + wpMain.width;\r
5465             rcMain.top    = wpMain.y;\r
5466             rcMain.bottom = wpMain.y + wpMain.height;\r
5467             \r
5468             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5469             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5470             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5471             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5472             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5473             wpMain.x = lpwp->x;\r
5474             wpMain.y = lpwp->y;\r
5475         }\r
5476     }\r
5477     break;\r
5478 \r
5479   /* [AS] Snapping */\r
5480   case WM_ENTERSIZEMOVE:\r
5481     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5482     if (hwnd == hwndMain) {\r
5483       doingSizing = TRUE;\r
5484       lastSizing = 0;\r
5485     }\r
5486     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5487     break;\r
5488 \r
5489   case WM_SIZING:\r
5490     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5491     if (hwnd == hwndMain) {\r
5492       lastSizing = wParam;\r
5493     }\r
5494     break;\r
5495 \r
5496   case WM_MOVING:\r
5497     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5498       return OnMoving( &sd, hwnd, wParam, lParam );\r
5499 \r
5500   case WM_EXITSIZEMOVE:\r
5501     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5502     if (hwnd == hwndMain) {\r
5503       RECT client;\r
5504       doingSizing = FALSE;\r
5505       InvalidateRect(hwnd, &boardRect, FALSE);\r
5506       GetClientRect(hwnd, &client);\r
5507       ResizeBoard(client.right, client.bottom, lastSizing);\r
5508       lastSizing = 0;\r
5509       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5510     }\r
5511     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5512     break;\r
5513 \r
5514   case WM_DESTROY: /* message: window being destroyed */\r
5515     PostQuitMessage(0);\r
5516     break;\r
5517 \r
5518   case WM_CLOSE:\r
5519     if (hwnd == hwndMain) {\r
5520       ExitEvent(0);\r
5521     }\r
5522     break;\r
5523 \r
5524   default:      /* Passes it on if unprocessed */\r
5525     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5526   }\r
5527   return 0;\r
5528 }\r
5529 \r
5530 /*---------------------------------------------------------------------------*\\r
5531  *\r
5532  * Misc utility routines\r
5533  *\r
5534 \*---------------------------------------------------------------------------*/\r
5535 \r
5536 /*\r
5537  * Decent random number generator, at least not as bad as Windows\r
5538  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5539  */\r
5540 unsigned int randstate;\r
5541 \r
5542 int\r
5543 myrandom(void)\r
5544 {\r
5545   randstate = randstate * 1664525 + 1013904223;\r
5546   return (int) randstate & 0x7fffffff;\r
5547 }\r
5548 \r
5549 void\r
5550 mysrandom(unsigned int seed)\r
5551 {\r
5552   randstate = seed;\r
5553 }\r
5554 \r
5555 \r
5556 /* \r
5557  * returns TRUE if user selects a different color, FALSE otherwise \r
5558  */\r
5559 \r
5560 BOOL\r
5561 ChangeColor(HWND hwnd, COLORREF *which)\r
5562 {\r
5563   static BOOL firstTime = TRUE;\r
5564   static DWORD customColors[16];\r
5565   CHOOSECOLOR cc;\r
5566   COLORREF newcolor;\r
5567   int i;\r
5568   ColorClass ccl;\r
5569 \r
5570   if (firstTime) {\r
5571     /* Make initial colors in use available as custom colors */\r
5572     /* Should we put the compiled-in defaults here instead? */\r
5573     i = 0;\r
5574     customColors[i++] = lightSquareColor & 0xffffff;\r
5575     customColors[i++] = darkSquareColor & 0xffffff;\r
5576     customColors[i++] = whitePieceColor & 0xffffff;\r
5577     customColors[i++] = blackPieceColor & 0xffffff;\r
5578     customColors[i++] = highlightSquareColor & 0xffffff;\r
5579     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5580 \r
5581     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5582       customColors[i++] = textAttribs[ccl].color;\r
5583     }\r
5584     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5585     firstTime = FALSE;\r
5586   }\r
5587 \r
5588   cc.lStructSize = sizeof(cc);\r
5589   cc.hwndOwner = hwnd;\r
5590   cc.hInstance = NULL;\r
5591   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5592   cc.lpCustColors = (LPDWORD) customColors;\r
5593   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5594 \r
5595   if (!ChooseColor(&cc)) return FALSE;\r
5596 \r
5597   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5598   if (newcolor == *which) return FALSE;\r
5599   *which = newcolor;\r
5600   return TRUE;\r
5601 \r
5602   /*\r
5603   InitDrawingColors();\r
5604   InvalidateRect(hwnd, &boardRect, FALSE);\r
5605   */\r
5606 }\r
5607 \r
5608 BOOLEAN\r
5609 MyLoadSound(MySound *ms)\r
5610 {\r
5611   BOOL ok = FALSE;\r
5612   struct stat st;\r
5613   FILE *f;\r
5614 \r
5615   if (ms->data) free(ms->data);\r
5616   ms->data = NULL;\r
5617 \r
5618   switch (ms->name[0]) {\r
5619   case NULLCHAR:\r
5620     /* Silence */\r
5621     ok = TRUE;\r
5622     break;\r
5623   case '$':\r
5624     /* System sound from Control Panel.  Don't preload here. */\r
5625     ok = TRUE;\r
5626     break;\r
5627   case '!':\r
5628     if (ms->name[1] == NULLCHAR) {\r
5629       /* "!" alone = silence */\r
5630       ok = TRUE;\r
5631     } else {\r
5632       /* Builtin wave resource.  Error if not found. */\r
5633       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5634       if (h == NULL) break;\r
5635       ms->data = (void *)LoadResource(hInst, h);\r
5636       if (h == NULL) break;\r
5637       ok = TRUE;\r
5638     }\r
5639     break;\r
5640   default:\r
5641     /* .wav file.  Error if not found. */\r
5642     f = fopen(ms->name, "rb");\r
5643     if (f == NULL) break;\r
5644     if (fstat(fileno(f), &st) < 0) break;\r
5645     ms->data = malloc(st.st_size);\r
5646     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5647     fclose(f);\r
5648     ok = TRUE;\r
5649     break;\r
5650   }\r
5651   if (!ok) {\r
5652     char buf[MSG_SIZ];\r
5653       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5654     DisplayError(buf, GetLastError());\r
5655   }\r
5656   return ok;\r
5657 }\r
5658 \r
5659 BOOLEAN\r
5660 MyPlaySound(MySound *ms)\r
5661 {\r
5662   BOOLEAN ok = FALSE;\r
5663 \r
5664   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5665   switch (ms->name[0]) {\r
5666   case NULLCHAR:\r
5667         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5668     /* Silence */\r
5669     ok = TRUE;\r
5670     break;\r
5671   case '$':\r
5672     /* System sound from Control Panel (deprecated feature).\r
5673        "$" alone or an unset sound name gets default beep (still in use). */\r
5674     if (ms->name[1]) {\r
5675       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5676     }\r
5677     if (!ok) ok = MessageBeep(MB_OK);\r
5678     break; \r
5679   case '!':\r
5680     /* Builtin wave resource, or "!" alone for silence */\r
5681     if (ms->name[1]) {\r
5682       if (ms->data == NULL) return FALSE;\r
5683       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5684     } else {\r
5685       ok = TRUE;\r
5686     }\r
5687     break;\r
5688   default:\r
5689     /* .wav file.  Error if not found. */\r
5690     if (ms->data == NULL) return FALSE;\r
5691     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5692     break;\r
5693   }\r
5694   /* Don't print an error: this can happen innocently if the sound driver\r
5695      is busy; for instance, if another instance of WinBoard is playing\r
5696      a sound at about the same time. */\r
5697   return ok;\r
5698 }\r
5699 \r
5700 \r
5701 LRESULT CALLBACK\r
5702 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5703 {\r
5704   BOOL ok;\r
5705   OPENFILENAME *ofn;\r
5706   static UINT *number; /* gross that this is static */\r
5707 \r
5708   switch (message) {\r
5709   case WM_INITDIALOG: /* message: initialize dialog box */\r
5710     /* Center the dialog over the application window */\r
5711     ofn = (OPENFILENAME *) lParam;\r
5712     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5713       number = (UINT *) ofn->lCustData;\r
5714       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5715     } else {\r
5716       number = NULL;\r
5717     }\r
5718     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5719     Translate(hDlg, 1536);\r
5720     return FALSE;  /* Allow for further processing */\r
5721 \r
5722   case WM_COMMAND:\r
5723     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5724       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5725     }\r
5726     return FALSE;  /* Allow for further processing */\r
5727   }\r
5728   return FALSE;\r
5729 }\r
5730 \r
5731 UINT APIENTRY\r
5732 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5733 {\r
5734   static UINT *number;\r
5735   OPENFILENAME *ofname;\r
5736   OFNOTIFY *ofnot;\r
5737   switch (uiMsg) {\r
5738   case WM_INITDIALOG:\r
5739     Translate(hdlg, DLG_IndexNumber);\r
5740     ofname = (OPENFILENAME *)lParam;\r
5741     number = (UINT *)(ofname->lCustData);\r
5742     break;\r
5743   case WM_NOTIFY:\r
5744     ofnot = (OFNOTIFY *)lParam;\r
5745     if (ofnot->hdr.code == CDN_FILEOK) {\r
5746       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5747     }\r
5748     break;\r
5749   }\r
5750   return 0;\r
5751 }\r
5752 \r
5753 \r
5754 FILE *\r
5755 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5756                char *nameFilt, char *dlgTitle, UINT *number,\r
5757                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5758 {\r
5759   OPENFILENAME openFileName;\r
5760   char buf1[MSG_SIZ];\r
5761   FILE *f;\r
5762 \r
5763   if (fileName == NULL) fileName = buf1;\r
5764   if (defName == NULL) {\r
5765     safeStrCpy(fileName, "*.", 3 );\r
5766     strcat(fileName, defExt);\r
5767   } else {\r
5768     safeStrCpy(fileName, defName, MSG_SIZ );\r
5769   }\r
5770     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5771   if (number) *number = 0;\r
5772 \r
5773   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5774   openFileName.hwndOwner         = hwnd;\r
5775   openFileName.hInstance         = (HANDLE) hInst;\r
5776   openFileName.lpstrFilter       = nameFilt;\r
5777   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5778   openFileName.nMaxCustFilter    = 0L;\r
5779   openFileName.nFilterIndex      = 1L;\r
5780   openFileName.lpstrFile         = fileName;\r
5781   openFileName.nMaxFile          = MSG_SIZ;\r
5782   openFileName.lpstrFileTitle    = fileTitle;\r
5783   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5784   openFileName.lpstrInitialDir   = NULL;\r
5785   openFileName.lpstrTitle        = dlgTitle;\r
5786   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5787     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5788     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5789     | (oldDialog ? 0 : OFN_EXPLORER);\r
5790   openFileName.nFileOffset       = 0;\r
5791   openFileName.nFileExtension    = 0;\r
5792   openFileName.lpstrDefExt       = defExt;\r
5793   openFileName.lCustData         = (LONG) number;\r
5794   openFileName.lpfnHook          = oldDialog ?\r
5795     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5796   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5797 \r
5798   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5799                         GetOpenFileName(&openFileName)) {\r
5800     /* open the file */\r
5801     f = fopen(openFileName.lpstrFile, write);\r
5802     if (f == NULL) {\r
5803       MessageBox(hwnd, _("File open failed"), NULL,\r
5804                  MB_OK|MB_ICONEXCLAMATION);\r
5805       return NULL;\r
5806     }\r
5807   } else {\r
5808     int err = CommDlgExtendedError();\r
5809     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5810     return FALSE;\r
5811   }\r
5812   return f;\r
5813 }\r
5814 \r
5815 \r
5816 \r
5817 VOID APIENTRY\r
5818 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5819 {\r
5820   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5821 \r
5822   /*\r
5823    * Get the first pop-up menu in the menu template. This is the\r
5824    * menu that TrackPopupMenu displays.\r
5825    */\r
5826   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5827   TranslateOneMenu(10, hmenuTrackPopup);\r
5828 \r
5829   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5830 \r
5831   /*\r
5832    * TrackPopup uses screen coordinates, so convert the\r
5833    * coordinates of the mouse click to screen coordinates.\r
5834    */\r
5835   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5836 \r
5837   /* Draw and track the floating pop-up menu. */\r
5838   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5839                  pt.x, pt.y, 0, hwnd, NULL);\r
5840 \r
5841   /* Destroy the menu.*/\r
5842   DestroyMenu(hmenu);\r
5843 }\r
5844    \r
5845 typedef struct {\r
5846   HWND hDlg, hText;\r
5847   int sizeX, sizeY, newSizeX, newSizeY;\r
5848   HDWP hdwp;\r
5849 } ResizeEditPlusButtonsClosure;\r
5850 \r
5851 BOOL CALLBACK\r
5852 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5853 {\r
5854   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5855   RECT rect;\r
5856   POINT pt;\r
5857 \r
5858   if (hChild == cl->hText) return TRUE;\r
5859   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5860   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5861   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5862   ScreenToClient(cl->hDlg, &pt);\r
5863   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5864     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5865   return TRUE;\r
5866 }\r
5867 \r
5868 /* Resize a dialog that has a (rich) edit field filling most of\r
5869    the top, with a row of buttons below */\r
5870 VOID\r
5871 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5872 {\r
5873   RECT rectText;\r
5874   int newTextHeight, newTextWidth;\r
5875   ResizeEditPlusButtonsClosure cl;\r
5876   \r
5877   /*if (IsIconic(hDlg)) return;*/\r
5878   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5879   \r
5880   cl.hdwp = BeginDeferWindowPos(8);\r
5881 \r
5882   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5883   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5884   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5885   if (newTextHeight < 0) {\r
5886     newSizeY += -newTextHeight;\r
5887     newTextHeight = 0;\r
5888   }\r
5889   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5890     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5891 \r
5892   cl.hDlg = hDlg;\r
5893   cl.hText = hText;\r
5894   cl.sizeX = sizeX;\r
5895   cl.sizeY = sizeY;\r
5896   cl.newSizeX = newSizeX;\r
5897   cl.newSizeY = newSizeY;\r
5898   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5899 \r
5900   EndDeferWindowPos(cl.hdwp);\r
5901 }\r
5902 \r
5903 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5904 {\r
5905     RECT    rChild, rParent;\r
5906     int     wChild, hChild, wParent, hParent;\r
5907     int     wScreen, hScreen, xNew, yNew;\r
5908     HDC     hdc;\r
5909 \r
5910     /* Get the Height and Width of the child window */\r
5911     GetWindowRect (hwndChild, &rChild);\r
5912     wChild = rChild.right - rChild.left;\r
5913     hChild = rChild.bottom - rChild.top;\r
5914 \r
5915     /* Get the Height and Width of the parent window */\r
5916     GetWindowRect (hwndParent, &rParent);\r
5917     wParent = rParent.right - rParent.left;\r
5918     hParent = rParent.bottom - rParent.top;\r
5919 \r
5920     /* Get the display limits */\r
5921     hdc = GetDC (hwndChild);\r
5922     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5923     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5924     ReleaseDC(hwndChild, hdc);\r
5925 \r
5926     /* Calculate new X position, then adjust for screen */\r
5927     xNew = rParent.left + ((wParent - wChild) /2);\r
5928     if (xNew < 0) {\r
5929         xNew = 0;\r
5930     } else if ((xNew+wChild) > wScreen) {\r
5931         xNew = wScreen - wChild;\r
5932     }\r
5933 \r
5934     /* Calculate new Y position, then adjust for screen */\r
5935     if( mode == 0 ) {\r
5936         yNew = rParent.top  + ((hParent - hChild) /2);\r
5937     }\r
5938     else {\r
5939         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5940     }\r
5941 \r
5942     if (yNew < 0) {\r
5943         yNew = 0;\r
5944     } else if ((yNew+hChild) > hScreen) {\r
5945         yNew = hScreen - hChild;\r
5946     }\r
5947 \r
5948     /* Set it, and return */\r
5949     return SetWindowPos (hwndChild, NULL,\r
5950                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5951 }\r
5952 \r
5953 /* Center one window over another */\r
5954 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
5955 {\r
5956     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
5957 }\r
5958 \r
5959 /*---------------------------------------------------------------------------*\\r
5960  *\r
5961  * Startup Dialog functions\r
5962  *\r
5963 \*---------------------------------------------------------------------------*/\r
5964 void\r
5965 InitComboStrings(HANDLE hwndCombo, char **cd)\r
5966 {\r
5967   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5968 \r
5969   while (*cd != NULL) {\r
5970     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
5971     cd++;\r
5972   }\r
5973 }\r
5974 \r
5975 void\r
5976 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
5977 {\r
5978   char buf1[MAX_ARG_LEN];\r
5979   int len;\r
5980 \r
5981   if (str[0] == '@') {\r
5982     FILE* f = fopen(str + 1, "r");\r
5983     if (f == NULL) {\r
5984       DisplayFatalError(str + 1, errno, 2);\r
5985       return;\r
5986     }\r
5987     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
5988     fclose(f);\r
5989     buf1[len] = NULLCHAR;\r
5990     str = buf1;\r
5991   }\r
5992 \r
5993   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5994 \r
5995   for (;;) {\r
5996     char buf[MSG_SIZ];\r
5997     char *end = strchr(str, '\n');\r
5998     if (end == NULL) return;\r
5999     memcpy(buf, str, end - str);\r
6000     buf[end - str] = NULLCHAR;\r
6001     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6002     str = end + 1;\r
6003   }\r
6004 }\r
6005 \r
6006 void\r
6007 SetStartupDialogEnables(HWND hDlg)\r
6008 {\r
6009   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6010     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6011     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6012   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6013     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6014   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6015     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6016   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6017     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6018   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6019     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6020     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6021     IsDlgButtonChecked(hDlg, OPT_View));\r
6022 }\r
6023 \r
6024 char *\r
6025 QuoteForFilename(char *filename)\r
6026 {\r
6027   int dquote, space;\r
6028   dquote = strchr(filename, '"') != NULL;\r
6029   space = strchr(filename, ' ') != NULL;\r
6030   if (dquote || space) {\r
6031     if (dquote) {\r
6032       return "'";\r
6033     } else {\r
6034       return "\"";\r
6035     }\r
6036   } else {\r
6037     return "";\r
6038   }\r
6039 }\r
6040 \r
6041 VOID\r
6042 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6043 {\r
6044   char buf[MSG_SIZ];\r
6045   char *q;\r
6046 \r
6047   InitComboStringsFromOption(hwndCombo, nthnames);\r
6048   q = QuoteForFilename(nthcp);\r
6049     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6050   if (*nthdir != NULLCHAR) {\r
6051     q = QuoteForFilename(nthdir);\r
6052       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6053   }\r
6054   if (*nthcp == NULLCHAR) {\r
6055     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6056   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6057     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6058     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6059   }\r
6060 }\r
6061 \r
6062 LRESULT CALLBACK\r
6063 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6064 {\r
6065   char buf[MSG_SIZ];\r
6066   HANDLE hwndCombo;\r
6067   char *p;\r
6068 \r
6069   switch (message) {\r
6070   case WM_INITDIALOG:\r
6071     /* Center the dialog */\r
6072     CenterWindow (hDlg, GetDesktopWindow());\r
6073     Translate(hDlg, DLG_Startup);\r
6074     /* Initialize the dialog items */\r
6075     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6076                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6077                   firstChessProgramNames);\r
6078     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6079                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6080                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6081     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6082     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6083       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6084     if (*appData.icsHelper != NULLCHAR) {\r
6085       char *q = QuoteForFilename(appData.icsHelper);\r
6086       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6087     }\r
6088     if (*appData.icsHost == NULLCHAR) {\r
6089       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6090       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6091     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6092       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6093       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6094     }\r
6095 \r
6096     if (appData.icsActive) {\r
6097       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6098     }\r
6099     else if (appData.noChessProgram) {\r
6100       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6101     }\r
6102     else {\r
6103       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6104     }\r
6105 \r
6106     SetStartupDialogEnables(hDlg);\r
6107     return TRUE;\r
6108 \r
6109   case WM_COMMAND:\r
6110     switch (LOWORD(wParam)) {\r
6111     case IDOK:\r
6112       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6113         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6114         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6115         p = buf;\r
6116         ParseArgs(StringGet, &p);\r
6117         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6118         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6119         p = buf;
6120         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6121         ParseArgs(StringGet, &p);\r
6122         SwapEngines(singleList); // ... and then make it 'second'\r
6123         appData.noChessProgram = FALSE;\r
6124         appData.icsActive = FALSE;\r
6125       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6126         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6127         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6128         p = buf;\r
6129         ParseArgs(StringGet, &p);\r
6130         if (appData.zippyPlay) {\r
6131           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6132           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6133           p = buf;\r
6134           ParseArgs(StringGet, &p);\r
6135         }\r
6136       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6137         appData.noChessProgram = TRUE;\r
6138         appData.icsActive = FALSE;\r
6139       } else {\r
6140         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6141                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6142         return TRUE;\r
6143       }\r
6144       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6145         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6146         p = buf;\r
6147         ParseArgs(StringGet, &p);\r
6148       }\r
6149       EndDialog(hDlg, TRUE);\r
6150       return TRUE;\r
6151 \r
6152     case IDCANCEL:\r
6153       ExitEvent(0);\r
6154       return TRUE;\r
6155 \r
6156     case IDM_HELPCONTENTS:\r
6157       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6158         MessageBox (GetFocus(),\r
6159                     _("Unable to activate help"),\r
6160                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6161       }\r
6162       break;\r
6163 \r
6164     default:\r
6165       SetStartupDialogEnables(hDlg);\r
6166       break;\r
6167     }\r
6168     break;\r
6169   }\r
6170   return FALSE;\r
6171 }\r
6172 \r
6173 /*---------------------------------------------------------------------------*\\r
6174  *\r
6175  * About box dialog functions\r
6176  *\r
6177 \*---------------------------------------------------------------------------*/\r
6178 \r
6179 /* Process messages for "About" dialog box */\r
6180 LRESULT CALLBACK\r
6181 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6182 {\r
6183   switch (message) {\r
6184   case WM_INITDIALOG: /* message: initialize dialog box */\r
6185     /* Center the dialog over the application window */\r
6186     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6187     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6188     Translate(hDlg, ABOUTBOX);\r
6189     JAWS_COPYRIGHT\r
6190     return (TRUE);\r
6191 \r
6192   case WM_COMMAND: /* message: received a command */\r
6193     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6194         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6195       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6196       return (TRUE);\r
6197     }\r
6198     break;\r
6199   }\r
6200   return (FALSE);\r
6201 }\r
6202 \r
6203 /*---------------------------------------------------------------------------*\\r
6204  *\r
6205  * Comment Dialog functions\r
6206  *\r
6207 \*---------------------------------------------------------------------------*/\r
6208 \r
6209 LRESULT CALLBACK\r
6210 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6211 {\r
6212   static HANDLE hwndText = NULL;\r
6213   int len, newSizeX, newSizeY, flags;\r
6214   static int sizeX, sizeY;\r
6215   char *str;\r
6216   RECT rect;\r
6217   MINMAXINFO *mmi;\r
6218 \r
6219   switch (message) {\r
6220   case WM_INITDIALOG: /* message: initialize dialog box */\r
6221     /* Initialize the dialog items */\r
6222     Translate(hDlg, DLG_EditComment);\r
6223     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6224     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6225     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6226     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6227     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6228     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6229     SetWindowText(hDlg, commentTitle);\r
6230     if (editComment) {\r
6231       SetFocus(hwndText);\r
6232     } else {\r
6233       SetFocus(GetDlgItem(hDlg, IDOK));\r
6234     }\r
6235     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6236                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6237                 MAKELPARAM(FALSE, 0));\r
6238     /* Size and position the dialog */\r
6239     if (!commentDialog) {\r
6240       commentDialog = hDlg;\r
6241       flags = SWP_NOZORDER;\r
6242       GetClientRect(hDlg, &rect);\r
6243       sizeX = rect.right;\r
6244       sizeY = rect.bottom;\r
6245       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6246           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6247         WINDOWPLACEMENT wp;\r
6248         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6249         wp.length = sizeof(WINDOWPLACEMENT);\r
6250         wp.flags = 0;\r
6251         wp.showCmd = SW_SHOW;\r
6252         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6253         wp.rcNormalPosition.left = wpComment.x;\r
6254         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6255         wp.rcNormalPosition.top = wpComment.y;\r
6256         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6257         SetWindowPlacement(hDlg, &wp);\r
6258 \r
6259         GetClientRect(hDlg, &rect);\r
6260         newSizeX = rect.right;\r
6261         newSizeY = rect.bottom;\r
6262         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6263                               newSizeX, newSizeY);\r
6264         sizeX = newSizeX;\r
6265         sizeY = newSizeY;\r
6266       }\r
6267     }\r
6268     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6269     return FALSE;\r
6270 \r
6271   case WM_COMMAND: /* message: received a command */\r
6272     switch (LOWORD(wParam)) {\r
6273     case IDOK:\r
6274       if (editComment) {\r
6275         char *p, *q;\r
6276         /* Read changed options from the dialog box */\r
6277         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6278         len = GetWindowTextLength(hwndText);\r
6279         str = (char *) malloc(len + 1);\r
6280         GetWindowText(hwndText, str, len + 1);\r
6281         p = q = str;\r
6282         while (*q) {\r
6283           if (*q == '\r')\r
6284             q++;\r
6285           else\r
6286             *p++ = *q++;\r
6287         }\r
6288         *p = NULLCHAR;\r
6289         ReplaceComment(commentIndex, str);\r
6290         free(str);\r
6291       }\r
6292       CommentPopDown();\r
6293       return TRUE;\r
6294 \r
6295     case IDCANCEL:\r
6296     case OPT_CancelComment:\r
6297       CommentPopDown();\r
6298       return TRUE;\r
6299 \r
6300     case OPT_ClearComment:\r
6301       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6302       break;\r
6303 \r
6304     case OPT_EditComment:\r
6305       EditCommentEvent();\r
6306       return TRUE;\r
6307 \r
6308     default:\r
6309       break;\r
6310     }\r
6311     break;\r
6312 \r
6313   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6314         if( wParam == OPT_CommentText ) {\r
6315             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6316 \r
6317             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6318                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6319                 POINTL pt;\r
6320                 LRESULT index;\r
6321 \r
6322                 pt.x = LOWORD( lpMF->lParam );\r
6323                 pt.y = HIWORD( lpMF->lParam );\r
6324 \r
6325                 if(lpMF->msg == WM_CHAR) {\r
6326                         CHARRANGE sel;\r
6327                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6328                         index = sel.cpMin;\r
6329                 } else\r
6330                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6331 \r
6332                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6333                 len = GetWindowTextLength(hwndText);\r
6334                 str = (char *) malloc(len + 1);\r
6335                 GetWindowText(hwndText, str, len + 1);\r
6336                 ReplaceComment(commentIndex, str);\r
6337                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6338                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6339                 free(str);\r
6340 \r
6341                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6342                 lpMF->msg = WM_USER;\r
6343 \r
6344                 return TRUE;\r
6345             }\r
6346         }\r
6347         break;\r
6348 \r
6349   case WM_SIZE:\r
6350     newSizeX = LOWORD(lParam);\r
6351     newSizeY = HIWORD(lParam);\r
6352     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6353     sizeX = newSizeX;\r
6354     sizeY = newSizeY;\r
6355     break;\r
6356 \r
6357   case WM_GETMINMAXINFO:\r
6358     /* Prevent resizing window too small */\r
6359     mmi = (MINMAXINFO *) lParam;\r
6360     mmi->ptMinTrackSize.x = 100;\r
6361     mmi->ptMinTrackSize.y = 100;\r
6362     break;\r
6363   }\r
6364   return FALSE;\r
6365 }\r
6366 \r
6367 VOID\r
6368 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6369 {\r
6370   FARPROC lpProc;\r
6371   char *p, *q;\r
6372 \r
6373   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6374 \r
6375   if (str == NULL) str = "";\r
6376   p = (char *) malloc(2 * strlen(str) + 2);\r
6377   q = p;\r
6378   while (*str) {\r
6379     if (*str == '\n') *q++ = '\r';\r
6380     *q++ = *str++;\r
6381   }\r
6382   *q = NULLCHAR;\r
6383   if (commentText != NULL) free(commentText);\r
6384 \r
6385   commentIndex = index;\r
6386   commentTitle = title;\r
6387   commentText = p;\r
6388   editComment = edit;\r
6389 \r
6390   if (commentDialog) {\r
6391     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6392     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6393   } else {\r
6394     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6395     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6396                  hwndMain, (DLGPROC)lpProc);\r
6397     FreeProcInstance(lpProc);\r
6398   }\r
6399   commentUp = TRUE;\r
6400 }\r
6401 \r
6402 \r
6403 /*---------------------------------------------------------------------------*\\r
6404  *\r
6405  * Type-in move dialog functions\r
6406  * \r
6407 \*---------------------------------------------------------------------------*/\r
6408 \r
6409 LRESULT CALLBACK\r
6410 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6411 {\r
6412   char move[MSG_SIZ];\r
6413   HWND hInput;\r
6414 \r
6415   switch (message) {\r
6416   case WM_INITDIALOG:\r
6417     move[0] = (char) lParam;\r
6418     move[1] = NULLCHAR;\r
6419     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6420     Translate(hDlg, DLG_TypeInMove);\r
6421     hInput = GetDlgItem(hDlg, OPT_Move);\r
6422     SetWindowText(hInput, move);\r
6423     SetFocus(hInput);\r
6424     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6425     return FALSE;\r
6426 \r
6427   case WM_COMMAND:\r
6428     switch (LOWORD(wParam)) {\r
6429     case IDOK:
6430 \r
6431       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6432       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
6433       TypeInDoneEvent(move);\r
6434       EndDialog(hDlg, TRUE);\r
6435       return TRUE;\r
6436     case IDCANCEL:\r
6437       EndDialog(hDlg, FALSE);\r
6438       return TRUE;\r
6439     default:\r
6440       break;\r
6441     }\r
6442     break;\r
6443   }\r
6444   return FALSE;\r
6445 }\r
6446 \r
6447 VOID\r
6448 PopUpMoveDialog(char firstchar)\r
6449 {\r
6450     FARPROC lpProc;\r
6451 \r
6452       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6453       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6454         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6455       FreeProcInstance(lpProc);\r
6456 }\r
6457 \r
6458 /*---------------------------------------------------------------------------*\\r
6459  *\r
6460  * Type-in name dialog functions\r
6461  * \r
6462 \*---------------------------------------------------------------------------*/\r
6463 \r
6464 LRESULT CALLBACK\r
6465 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6466 {\r
6467   char move[MSG_SIZ];\r
6468   HWND hInput;\r
6469 \r
6470   switch (message) {\r
6471   case WM_INITDIALOG:\r
6472     move[0] = (char) lParam;\r
6473     move[1] = NULLCHAR;\r
6474     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6475     Translate(hDlg, DLG_TypeInName);\r
6476     hInput = GetDlgItem(hDlg, OPT_Name);\r
6477     SetWindowText(hInput, move);\r
6478     SetFocus(hInput);\r
6479     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6480     return FALSE;\r
6481 \r
6482   case WM_COMMAND:\r
6483     switch (LOWORD(wParam)) {\r
6484     case IDOK:\r
6485       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6486       appData.userName = strdup(move);\r
6487       SetUserLogo();\r
6488       SetGameInfo();\r
6489       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6490         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6491         DisplayTitle(move);\r
6492       }\r
6493 \r
6494 \r
6495       EndDialog(hDlg, TRUE);\r
6496       return TRUE;\r
6497     case IDCANCEL:\r
6498       EndDialog(hDlg, FALSE);\r
6499       return TRUE;\r
6500     default:\r
6501       break;\r
6502     }\r
6503     break;\r
6504   }\r
6505   return FALSE;\r
6506 }\r
6507 \r
6508 VOID\r
6509 PopUpNameDialog(char firstchar)\r
6510 {\r
6511     FARPROC lpProc;\r
6512     \r
6513       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6514       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6515         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6516       FreeProcInstance(lpProc);\r
6517 }\r
6518 \r
6519 /*---------------------------------------------------------------------------*\\r
6520  *\r
6521  *  Error dialogs\r
6522  * \r
6523 \*---------------------------------------------------------------------------*/\r
6524 \r
6525 /* Nonmodal error box */\r
6526 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6527                              WPARAM wParam, LPARAM lParam);\r
6528 \r
6529 VOID\r
6530 ErrorPopUp(char *title, char *content)\r
6531 {\r
6532   FARPROC lpProc;\r
6533   char *p, *q;\r
6534   BOOLEAN modal = hwndMain == NULL;\r
6535 \r
6536   p = content;\r
6537   q = errorMessage;\r
6538   while (*p) {\r
6539     if (*p == '\n') {\r
6540       if (modal) {\r
6541         *q++ = ' ';\r
6542         p++;\r
6543       } else {\r
6544         *q++ = '\r';\r
6545         *q++ = *p++;\r
6546       }\r
6547     } else {\r
6548       *q++ = *p++;\r
6549     }\r
6550   }\r
6551   *q = NULLCHAR;\r
6552   strncpy(errorTitle, title, sizeof(errorTitle));\r
6553   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6554   \r
6555   if (modal) {\r
6556     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6557   } else {\r
6558     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6559     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6560                  hwndMain, (DLGPROC)lpProc);\r
6561     FreeProcInstance(lpProc);\r
6562   }\r
6563 }\r
6564 \r
6565 VOID\r
6566 ErrorPopDown()\r
6567 {\r
6568   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6569   if (errorDialog == NULL) return;\r
6570   DestroyWindow(errorDialog);\r
6571   errorDialog = NULL;\r
6572   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6573 }\r
6574 \r
6575 LRESULT CALLBACK\r
6576 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6577 {\r
6578   HANDLE hwndText;\r
6579   RECT rChild;\r
6580 \r
6581   switch (message) {\r
6582   case WM_INITDIALOG:\r
6583     GetWindowRect(hDlg, &rChild);\r
6584 \r
6585     /*\r
6586     SetWindowPos(hDlg, NULL, rChild.left,\r
6587       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6588       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6589     */\r
6590 \r
6591     /* \r
6592         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6593         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6594         and it doesn't work when you resize the dialog.\r
6595         For now, just give it a default position.\r
6596     */\r
6597     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6598     Translate(hDlg, DLG_Error);\r
6599 \r
6600     errorDialog = hDlg;\r
6601     SetWindowText(hDlg, errorTitle);\r
6602     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6603     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6604     return FALSE;\r
6605 \r
6606   case WM_COMMAND:\r
6607     switch (LOWORD(wParam)) {\r
6608     case IDOK:\r
6609     case IDCANCEL:\r
6610       if (errorDialog == hDlg) errorDialog = NULL;\r
6611       DestroyWindow(hDlg);\r
6612       return TRUE;\r
6613 \r
6614     default:\r
6615       break;\r
6616     }\r
6617     break;\r
6618   }\r
6619   return FALSE;\r
6620 }\r
6621 \r
6622 #ifdef GOTHIC\r
6623 HWND gothicDialog = NULL;\r
6624 \r
6625 LRESULT CALLBACK\r
6626 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6627 {\r
6628   HANDLE hwndText;\r
6629   RECT rChild;\r
6630   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6631 \r
6632   switch (message) {\r
6633   case WM_INITDIALOG:\r
6634     GetWindowRect(hDlg, &rChild);\r
6635 \r
6636     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6637                                                              SWP_NOZORDER);\r
6638 \r
6639     /* \r
6640         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6641         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6642         and it doesn't work when you resize the dialog.\r
6643         For now, just give it a default position.\r
6644     */\r
6645     gothicDialog = hDlg;\r
6646     SetWindowText(hDlg, errorTitle);\r
6647     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6648     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6649     return FALSE;\r
6650 \r
6651   case WM_COMMAND:\r
6652     switch (LOWORD(wParam)) {\r
6653     case IDOK:\r
6654     case IDCANCEL:\r
6655       if (errorDialog == hDlg) errorDialog = NULL;\r
6656       DestroyWindow(hDlg);\r
6657       return TRUE;\r
6658 \r
6659     default:\r
6660       break;\r
6661     }\r
6662     break;\r
6663   }\r
6664   return FALSE;\r
6665 }\r
6666 \r
6667 VOID\r
6668 GothicPopUp(char *title, VariantClass variant)\r
6669 {\r
6670   FARPROC lpProc;\r
6671   static char *lastTitle;\r
6672 \r
6673   strncpy(errorTitle, title, sizeof(errorTitle));\r
6674   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6675 \r
6676   if(lastTitle != title && gothicDialog != NULL) {\r
6677     DestroyWindow(gothicDialog);\r
6678     gothicDialog = NULL;\r
6679   }\r
6680   if(variant != VariantNormal && gothicDialog == NULL) {\r
6681     title = lastTitle;\r
6682     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6683     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6684                  hwndMain, (DLGPROC)lpProc);\r
6685     FreeProcInstance(lpProc);\r
6686   }\r
6687 }\r
6688 #endif\r
6689 \r
6690 /*---------------------------------------------------------------------------*\\r
6691  *\r
6692  *  Ics Interaction console functions\r
6693  *\r
6694 \*---------------------------------------------------------------------------*/\r
6695 \r
6696 #define HISTORY_SIZE 64\r
6697 static char *history[HISTORY_SIZE];\r
6698 int histIn = 0, histP = 0;\r
6699 \r
6700 VOID\r
6701 SaveInHistory(char *cmd)\r
6702 {\r
6703   if (history[histIn] != NULL) {\r
6704     free(history[histIn]);\r
6705     history[histIn] = NULL;\r
6706   }\r
6707   if (*cmd == NULLCHAR) return;\r
6708   history[histIn] = StrSave(cmd);\r
6709   histIn = (histIn + 1) % HISTORY_SIZE;\r
6710   if (history[histIn] != NULL) {\r
6711     free(history[histIn]);\r
6712     history[histIn] = NULL;\r
6713   }\r
6714   histP = histIn;\r
6715 }\r
6716 \r
6717 char *\r
6718 PrevInHistory(char *cmd)\r
6719 {\r
6720   int newhp;\r
6721   if (histP == histIn) {\r
6722     if (history[histIn] != NULL) free(history[histIn]);\r
6723     history[histIn] = StrSave(cmd);\r
6724   }\r
6725   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6726   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6727   histP = newhp;\r
6728   return history[histP];\r
6729 }\r
6730 \r
6731 char *\r
6732 NextInHistory()\r
6733 {\r
6734   if (histP == histIn) return NULL;\r
6735   histP = (histP + 1) % HISTORY_SIZE;\r
6736   return history[histP];   \r
6737 }\r
6738 \r
6739 HMENU\r
6740 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6741 {\r
6742   HMENU hmenu, h;\r
6743   int i = 0;\r
6744   hmenu = LoadMenu(hInst, "TextMenu");\r
6745   h = GetSubMenu(hmenu, 0);\r
6746   while (e->item) {\r
6747     if (strcmp(e->item, "-") == 0) {\r
6748       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6749     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6750       int flags = MF_STRING, j = 0;\r
6751       if (e->item[0] == '|') {\r
6752         flags |= MF_MENUBARBREAK;\r
6753         j++;\r
6754       }\r
6755       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6756       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6757     }\r
6758     e++;\r
6759     i++;\r
6760   } \r
6761   return hmenu;\r
6762 }\r
6763 \r
6764 WNDPROC consoleTextWindowProc;\r
6765 \r
6766 void\r
6767 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6768 {\r
6769   char buf[MSG_SIZ], name[MSG_SIZ];\r
6770   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6771   CHARRANGE sel;\r
6772 \r
6773   if (!getname) {\r
6774     SetWindowText(hInput, command);\r
6775     if (immediate) {\r
6776       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6777     } else {\r
6778       sel.cpMin = 999999;\r
6779       sel.cpMax = 999999;\r
6780       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6781       SetFocus(hInput);\r
6782     }\r
6783     return;\r
6784   }    \r
6785   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6786   if (sel.cpMin == sel.cpMax) {\r
6787     /* Expand to surrounding word */\r
6788     TEXTRANGE tr;\r
6789     do {\r
6790       tr.chrg.cpMax = sel.cpMin;\r
6791       tr.chrg.cpMin = --sel.cpMin;\r
6792       if (sel.cpMin < 0) break;\r
6793       tr.lpstrText = name;\r
6794       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6795     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6796     sel.cpMin++;\r
6797 \r
6798     do {\r
6799       tr.chrg.cpMin = sel.cpMax;\r
6800       tr.chrg.cpMax = ++sel.cpMax;\r
6801       tr.lpstrText = name;\r
6802       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6803     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6804     sel.cpMax--;\r
6805 \r
6806     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6807       MessageBeep(MB_ICONEXCLAMATION);\r
6808       return;\r
6809     }\r
6810     tr.chrg = sel;\r
6811     tr.lpstrText = name;\r
6812     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6813   } else {\r
6814     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6815       MessageBeep(MB_ICONEXCLAMATION);\r
6816       return;\r
6817     }\r
6818     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6819   }\r
6820   if (immediate) {\r
6821     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6822     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6823     SetWindowText(hInput, buf);\r
6824     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6825   } else {\r
6826     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6827       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6828     SetWindowText(hInput, buf);\r
6829     sel.cpMin = 999999;\r
6830     sel.cpMax = 999999;\r
6831     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6832     SetFocus(hInput);\r
6833   }\r
6834 }\r
6835 \r
6836 LRESULT CALLBACK \r
6837 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6838 {\r
6839   HWND hInput;\r
6840   CHARRANGE sel;\r
6841 \r
6842   switch (message) {\r
6843   case WM_KEYDOWN:\r
6844     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6845     if(wParam=='R') return 0;\r
6846     switch (wParam) {\r
6847     case VK_PRIOR:\r
6848       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6849       return 0;\r
6850     case VK_NEXT:\r
6851       sel.cpMin = 999999;\r
6852       sel.cpMax = 999999;\r
6853       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6854       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6855       return 0;\r
6856     }\r
6857     break;\r
6858   case WM_CHAR:\r
6859    if(wParam != '\022') {\r
6860     if (wParam == '\t') {\r
6861       if (GetKeyState(VK_SHIFT) < 0) {\r
6862         /* shifted */\r
6863         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6864         if (buttonDesc[0].hwnd) {\r
6865           SetFocus(buttonDesc[0].hwnd);\r
6866         } else {\r
6867           SetFocus(hwndMain);\r
6868         }\r
6869       } else {\r
6870         /* unshifted */\r
6871         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6872       }\r
6873     } else {\r
6874       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6875       JAWS_DELETE( SetFocus(hInput); )\r
6876       SendMessage(hInput, message, wParam, lParam);\r
6877     }\r
6878     return 0;\r
6879    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
6880    lParam = -1;\r
6881   case WM_RBUTTONDOWN:\r
6882     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6883       /* Move selection here if it was empty */\r
6884       POINT pt;\r
6885       pt.x = LOWORD(lParam);\r
6886       pt.y = HIWORD(lParam);\r
6887       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6888       if (sel.cpMin == sel.cpMax) {\r
6889         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6890         sel.cpMax = sel.cpMin;\r
6891         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6892       }\r
6893       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6894 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6895       POINT pt;\r
6896       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6897       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6898       if (sel.cpMin == sel.cpMax) {\r
6899         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6900         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6901       }\r
6902       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6903         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6904       }\r
6905       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6906       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6907       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6908       MenuPopup(hwnd, pt, hmenu, -1);\r
6909 }\r
6910     }\r
6911     return 0;\r
6912   case WM_RBUTTONUP:\r
6913     if (GetKeyState(VK_SHIFT) & ~1) {\r
6914       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6915         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6916     }\r
6917     return 0;\r
6918   case WM_PASTE:\r
6919     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6920     SetFocus(hInput);\r
6921     return SendMessage(hInput, message, wParam, lParam);\r
6922   case WM_MBUTTONDOWN:\r
6923     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6924   case WM_COMMAND:\r
6925     switch (LOWORD(wParam)) {\r
6926     case IDM_QuickPaste:\r
6927       {\r
6928         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6929         if (sel.cpMin == sel.cpMax) {\r
6930           MessageBeep(MB_ICONEXCLAMATION);\r
6931           return 0;\r
6932         }\r
6933         SendMessage(hwnd, WM_COPY, 0, 0);\r
6934         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6935         SendMessage(hInput, WM_PASTE, 0, 0);\r
6936         SetFocus(hInput);\r
6937         return 0;\r
6938       }\r
6939     case IDM_Cut:\r
6940       SendMessage(hwnd, WM_CUT, 0, 0);\r
6941       return 0;\r
6942     case IDM_Paste:\r
6943       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6944       return 0;\r
6945     case IDM_Copy:\r
6946       SendMessage(hwnd, WM_COPY, 0, 0);\r
6947       return 0;\r
6948     default:\r
6949       {\r
6950         int i = LOWORD(wParam) - IDM_CommandX;\r
6951         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6952             icsTextMenuEntry[i].command != NULL) {\r
6953           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6954                    icsTextMenuEntry[i].getname,\r
6955                    icsTextMenuEntry[i].immediate);\r
6956           return 0;\r
6957         }\r
6958       }\r
6959       break;\r
6960     }\r
6961     break;\r
6962   }\r
6963   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
6964 }\r
6965 \r
6966 WNDPROC consoleInputWindowProc;\r
6967 \r
6968 LRESULT CALLBACK\r
6969 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6970 {\r
6971   char buf[MSG_SIZ];\r
6972   char *p;\r
6973   static BOOL sendNextChar = FALSE;\r
6974   static BOOL quoteNextChar = FALSE;\r
6975   InputSource *is = consoleInputSource;\r
6976   CHARFORMAT cf;\r
6977   CHARRANGE sel;\r
6978 \r
6979   switch (message) {\r
6980   case WM_CHAR:\r
6981     if (!appData.localLineEditing || sendNextChar) {\r
6982       is->buf[0] = (CHAR) wParam;\r
6983       is->count = 1;\r
6984       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6985       sendNextChar = FALSE;\r
6986       return 0;\r
6987     }\r
6988     if (quoteNextChar) {\r
6989       buf[0] = (char) wParam;\r
6990       buf[1] = NULLCHAR;\r
6991       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
6992       quoteNextChar = FALSE;\r
6993       return 0;\r
6994     }\r
6995     switch (wParam) {\r
6996     case '\r':   /* Enter key */\r
6997       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
6998       if (consoleEcho) SaveInHistory(is->buf);\r
6999       is->buf[is->count++] = '\n';\r
7000       is->buf[is->count] = NULLCHAR;\r
7001       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7002       if (consoleEcho) {\r
7003         ConsoleOutput(is->buf, is->count, TRUE);\r
7004       } else if (appData.localLineEditing) {\r
7005         ConsoleOutput("\n", 1, TRUE);\r
7006       }\r
7007       /* fall thru */\r
7008     case '\033': /* Escape key */\r
7009       SetWindowText(hwnd, "");\r
7010       cf.cbSize = sizeof(CHARFORMAT);\r
7011       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7012       if (consoleEcho) {\r
7013         cf.crTextColor = textAttribs[ColorNormal].color;\r
7014       } else {\r
7015         cf.crTextColor = COLOR_ECHOOFF;\r
7016       }\r
7017       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7018       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7019       return 0;\r
7020     case '\t':   /* Tab key */\r
7021       if (GetKeyState(VK_SHIFT) < 0) {\r
7022         /* shifted */\r
7023         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7024       } else {\r
7025         /* unshifted */\r
7026         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7027         if (buttonDesc[0].hwnd) {\r
7028           SetFocus(buttonDesc[0].hwnd);\r
7029         } else {\r
7030           SetFocus(hwndMain);\r
7031         }\r
7032       }\r
7033       return 0;\r
7034     case '\023': /* Ctrl+S */\r
7035       sendNextChar = TRUE;\r
7036       return 0;\r
7037     case '\021': /* Ctrl+Q */\r
7038       quoteNextChar = TRUE;\r
7039       return 0;\r
7040     JAWS_REPLAY\r
7041     default:\r
7042       break;\r
7043     }\r
7044     break;\r
7045   case WM_KEYDOWN:\r
7046     switch (wParam) {\r
7047     case VK_UP:\r
7048       GetWindowText(hwnd, buf, MSG_SIZ);\r
7049       p = PrevInHistory(buf);\r
7050       if (p != NULL) {\r
7051         SetWindowText(hwnd, p);\r
7052         sel.cpMin = 999999;\r
7053         sel.cpMax = 999999;\r
7054         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7055         return 0;\r
7056       }\r
7057       break;\r
7058     case VK_DOWN:\r
7059       p = NextInHistory();\r
7060       if (p != NULL) {\r
7061         SetWindowText(hwnd, p);\r
7062         sel.cpMin = 999999;\r
7063         sel.cpMax = 999999;\r
7064         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7065         return 0;\r
7066       }\r
7067       break;\r
7068     case VK_HOME:\r
7069     case VK_END:\r
7070       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7071       /* fall thru */\r
7072     case VK_PRIOR:\r
7073     case VK_NEXT:\r
7074       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7075       return 0;\r
7076     }\r
7077     break;\r
7078   case WM_MBUTTONDOWN:\r
7079     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7080       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7081     break;\r
7082   case WM_RBUTTONUP:\r
7083     if (GetKeyState(VK_SHIFT) & ~1) {\r
7084       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7085         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7086     } else {\r
7087       POINT pt;\r
7088       HMENU hmenu;\r
7089       hmenu = LoadMenu(hInst, "InputMenu");\r
7090       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7091       if (sel.cpMin == sel.cpMax) {\r
7092         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7093         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7094       }\r
7095       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7096         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7097       }\r
7098       pt.x = LOWORD(lParam);\r
7099       pt.y = HIWORD(lParam);\r
7100       MenuPopup(hwnd, pt, hmenu, -1);\r
7101     }\r
7102     return 0;\r
7103   case WM_COMMAND:\r
7104     switch (LOWORD(wParam)) { \r
7105     case IDM_Undo:\r
7106       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7107       return 0;\r
7108     case IDM_SelectAll:\r
7109       sel.cpMin = 0;\r
7110       sel.cpMax = -1; /*999999?*/\r
7111       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7112       return 0;\r
7113     case IDM_Cut:\r
7114       SendMessage(hwnd, WM_CUT, 0, 0);\r
7115       return 0;\r
7116     case IDM_Paste:\r
7117       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7118       return 0;\r
7119     case IDM_Copy:\r
7120       SendMessage(hwnd, WM_COPY, 0, 0);\r
7121       return 0;\r
7122     }\r
7123     break;\r
7124   }\r
7125   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7126 }\r
7127 \r
7128 #define CO_MAX  100000\r
7129 #define CO_TRIM   1000\r
7130 \r
7131 LRESULT CALLBACK\r
7132 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7133 {\r
7134   static SnapData sd;\r
7135   HWND hText, hInput;\r
7136   RECT rect;\r
7137   static int sizeX, sizeY;\r
7138   int newSizeX, newSizeY;\r
7139   MINMAXINFO *mmi;\r
7140   WORD wMask;\r
7141 \r
7142   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7143   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7144 \r
7145   switch (message) {\r
7146   case WM_NOTIFY:\r
7147     if (((NMHDR*)lParam)->code == EN_LINK)\r
7148     {\r
7149       ENLINK *pLink = (ENLINK*)lParam;\r
7150       if (pLink->msg == WM_LBUTTONUP)\r
7151       {\r
7152         TEXTRANGE tr;\r
7153 \r
7154         tr.chrg = pLink->chrg;\r
7155         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7156         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7157         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7158         free(tr.lpstrText);\r
7159       }\r
7160     }\r
7161     break;\r
7162   case WM_INITDIALOG: /* message: initialize dialog box */\r
7163     hwndConsole = hDlg;\r
7164     SetFocus(hInput);\r
7165     consoleTextWindowProc = (WNDPROC)\r
7166       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7167     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7168     consoleInputWindowProc = (WNDPROC)\r
7169       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7170     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7171     Colorize(ColorNormal, TRUE);\r
7172     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7173     ChangedConsoleFont();\r
7174     GetClientRect(hDlg, &rect);\r
7175     sizeX = rect.right;\r
7176     sizeY = rect.bottom;\r
7177     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7178         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7179       WINDOWPLACEMENT wp;\r
7180       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7181       wp.length = sizeof(WINDOWPLACEMENT);\r
7182       wp.flags = 0;\r
7183       wp.showCmd = SW_SHOW;\r
7184       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7185       wp.rcNormalPosition.left = wpConsole.x;\r
7186       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7187       wp.rcNormalPosition.top = wpConsole.y;\r
7188       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7189       SetWindowPlacement(hDlg, &wp);\r
7190     }\r
7191 \r
7192    // [HGM] Chessknight's change 2004-07-13\r
7193    else { /* Determine Defaults */\r
7194        WINDOWPLACEMENT wp;\r
7195        wpConsole.x = wpMain.width + 1;\r
7196        wpConsole.y = wpMain.y;\r
7197        wpConsole.width = screenWidth -  wpMain.width;\r
7198        wpConsole.height = wpMain.height;\r
7199        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7200        wp.length = sizeof(WINDOWPLACEMENT);\r
7201        wp.flags = 0;\r
7202        wp.showCmd = SW_SHOW;\r
7203        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7204        wp.rcNormalPosition.left = wpConsole.x;\r
7205        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7206        wp.rcNormalPosition.top = wpConsole.y;\r
7207        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7208        SetWindowPlacement(hDlg, &wp);\r
7209     }\r
7210 \r
7211    // Allow hText to highlight URLs and send notifications on them\r
7212    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7213    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7214    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7215    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7216 \r
7217     return FALSE;\r
7218 \r
7219   case WM_SETFOCUS:\r
7220     SetFocus(hInput);\r
7221     return 0;\r
7222 \r
7223   case WM_CLOSE:\r
7224     ExitEvent(0);\r
7225     /* not reached */\r
7226     break;\r
7227 \r
7228   case WM_SIZE:\r
7229     if (IsIconic(hDlg)) break;\r
7230     newSizeX = LOWORD(lParam);\r
7231     newSizeY = HIWORD(lParam);\r
7232     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7233       RECT rectText, rectInput;\r
7234       POINT pt;\r
7235       int newTextHeight, newTextWidth;\r
7236       GetWindowRect(hText, &rectText);\r
7237       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7238       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7239       if (newTextHeight < 0) {\r
7240         newSizeY += -newTextHeight;\r
7241         newTextHeight = 0;\r
7242       }\r
7243       SetWindowPos(hText, NULL, 0, 0,\r
7244         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7245       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7246       pt.x = rectInput.left;\r
7247       pt.y = rectInput.top + newSizeY - sizeY;\r
7248       ScreenToClient(hDlg, &pt);\r
7249       SetWindowPos(hInput, NULL, \r
7250         pt.x, pt.y, /* needs client coords */   \r
7251         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7252         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7253     }\r
7254     sizeX = newSizeX;\r
7255     sizeY = newSizeY;\r
7256     break;\r
7257 \r
7258   case WM_GETMINMAXINFO:\r
7259     /* Prevent resizing window too small */\r
7260     mmi = (MINMAXINFO *) lParam;\r
7261     mmi->ptMinTrackSize.x = 100;\r
7262     mmi->ptMinTrackSize.y = 100;\r
7263     break;\r
7264 \r
7265   /* [AS] Snapping */\r
7266   case WM_ENTERSIZEMOVE:\r
7267     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7268 \r
7269   case WM_SIZING:\r
7270     return OnSizing( &sd, hDlg, wParam, lParam );\r
7271 \r
7272   case WM_MOVING:\r
7273     return OnMoving( &sd, hDlg, wParam, lParam );\r
7274 \r
7275   case WM_EXITSIZEMOVE:\r
7276         UpdateICSWidth(hText);\r
7277     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7278   }\r
7279 \r
7280   return DefWindowProc(hDlg, message, wParam, lParam);\r
7281 }\r
7282 \r
7283 \r
7284 VOID\r
7285 ConsoleCreate()\r
7286 {\r
7287   HWND hCons;\r
7288   if (hwndConsole) return;\r
7289   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7290   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7291 }\r
7292 \r
7293 \r
7294 VOID\r
7295 ConsoleOutput(char* data, int length, int forceVisible)\r
7296 {\r
7297   HWND hText;\r
7298   int trim, exlen;\r
7299   char *p, *q;\r
7300   char buf[CO_MAX+1];\r
7301   POINT pEnd;\r
7302   RECT rect;\r
7303   static int delayLF = 0;\r
7304   CHARRANGE savesel, sel;\r
7305 \r
7306   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7307   p = data;\r
7308   q = buf;\r
7309   if (delayLF) {\r
7310     *q++ = '\r';\r
7311     *q++ = '\n';\r
7312     delayLF = 0;\r
7313   }\r
7314   while (length--) {\r
7315     if (*p == '\n') {\r
7316       if (*++p) {\r
7317         *q++ = '\r';\r
7318         *q++ = '\n';\r
7319       } else {\r
7320         delayLF = 1;\r
7321       }\r
7322     } else if (*p == '\007') {\r
7323        MyPlaySound(&sounds[(int)SoundBell]);\r
7324        p++;\r
7325     } else {\r
7326       *q++ = *p++;\r
7327     }\r
7328   }\r
7329   *q = NULLCHAR;\r
7330   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7331   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7332   /* Save current selection */\r
7333   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7334   exlen = GetWindowTextLength(hText);\r
7335   /* Find out whether current end of text is visible */\r
7336   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7337   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7338   /* Trim existing text if it's too long */\r
7339   if (exlen + (q - buf) > CO_MAX) {\r
7340     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7341     sel.cpMin = 0;\r
7342     sel.cpMax = trim;\r
7343     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7344     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7345     exlen -= trim;\r
7346     savesel.cpMin -= trim;\r
7347     savesel.cpMax -= trim;\r
7348     if (exlen < 0) exlen = 0;\r
7349     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7350     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7351   }\r
7352   /* Append the new text */\r
7353   sel.cpMin = exlen;\r
7354   sel.cpMax = exlen;\r
7355   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7356   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7357   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7358   if (forceVisible || exlen == 0 ||\r
7359       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7360        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7361     /* Scroll to make new end of text visible if old end of text\r
7362        was visible or new text is an echo of user typein */\r
7363     sel.cpMin = 9999999;\r
7364     sel.cpMax = 9999999;\r
7365     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7366     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7367     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7368     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7369   }\r
7370   if (savesel.cpMax == exlen || forceVisible) {\r
7371     /* Move insert point to new end of text if it was at the old\r
7372        end of text or if the new text is an echo of user typein */\r
7373     sel.cpMin = 9999999;\r
7374     sel.cpMax = 9999999;\r
7375     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7376   } else {\r
7377     /* Restore previous selection */\r
7378     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7379   }\r
7380   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7381 }\r
7382 \r
7383 /*---------*/\r
7384 \r
7385 \r
7386 void\r
7387 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7388 {\r
7389   char buf[100];\r
7390   char *str;\r
7391   COLORREF oldFg, oldBg;\r
7392   HFONT oldFont;\r
7393   RECT rect;\r
7394 \r
7395   if(copyNumber > 1)
7396     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7397 \r
7398   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7399   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7400   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7401 \r
7402   rect.left = x;\r
7403   rect.right = x + squareSize;\r
7404   rect.top  = y;\r
7405   rect.bottom = y + squareSize;\r
7406   str = buf;\r
7407 \r
7408   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7409                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7410              y, ETO_CLIPPED|ETO_OPAQUE,\r
7411              &rect, str, strlen(str), NULL);\r
7412 \r
7413   (void) SetTextColor(hdc, oldFg);\r
7414   (void) SetBkColor(hdc, oldBg);\r
7415   (void) SelectObject(hdc, oldFont);\r
7416 }\r
7417 \r
7418 void\r
7419 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7420               RECT *rect, char *color, char *flagFell)\r
7421 {\r
7422   char buf[100];\r
7423   char *str;\r
7424   COLORREF oldFg, oldBg;\r
7425   HFONT oldFont;\r
7426 \r
7427   if (appData.clockMode) {\r
7428     if (tinyLayout)\r
7429       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7430     else\r
7431       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7432     str = buf;\r
7433   } else {\r
7434     str = color;\r
7435   }\r
7436 \r
7437   if (highlight) {\r
7438     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7439     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7440   } else {\r
7441     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7442     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7443   }\r
7444   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7445 \r
7446   JAWS_SILENCE\r
7447 \r
7448   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7449              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7450              rect, str, strlen(str), NULL);\r
7451   if(logoHeight > 0 && appData.clockMode) {\r
7452       RECT r;\r
7453       str += strlen(color)+2;\r
7454       r.top = rect->top + logoHeight/2;\r
7455       r.left = rect->left;\r
7456       r.right = rect->right;\r
7457       r.bottom = rect->bottom;\r
7458       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7459                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7460                  &r, str, strlen(str), NULL);\r
7461   }\r
7462   (void) SetTextColor(hdc, oldFg);\r
7463   (void) SetBkColor(hdc, oldBg);\r
7464   (void) SelectObject(hdc, oldFont);\r
7465 }\r
7466 \r
7467 \r
7468 int\r
7469 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7470            OVERLAPPED *ovl)\r
7471 {\r
7472   int ok, err;\r
7473 \r
7474   /* [AS]  */\r
7475   if( count <= 0 ) {\r
7476     if (appData.debugMode) {\r
7477       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7478     }\r
7479 \r
7480     return ERROR_INVALID_USER_BUFFER;\r
7481   }\r
7482 \r
7483   ResetEvent(ovl->hEvent);\r
7484   ovl->Offset = ovl->OffsetHigh = 0;\r
7485   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7486   if (ok) {\r
7487     err = NO_ERROR;\r
7488   } else {\r
7489     err = GetLastError();\r
7490     if (err == ERROR_IO_PENDING) {\r
7491       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7492       if (ok)\r
7493         err = NO_ERROR;\r
7494       else\r
7495         err = GetLastError();\r
7496     }\r
7497   }\r
7498   return err;\r
7499 }\r
7500 \r
7501 int\r
7502 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7503             OVERLAPPED *ovl)\r
7504 {\r
7505   int ok, err;\r
7506 \r
7507   ResetEvent(ovl->hEvent);\r
7508   ovl->Offset = ovl->OffsetHigh = 0;\r
7509   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7510   if (ok) {\r
7511     err = NO_ERROR;\r
7512   } else {\r
7513     err = GetLastError();\r
7514     if (err == ERROR_IO_PENDING) {\r
7515       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7516       if (ok)\r
7517         err = NO_ERROR;\r
7518       else\r
7519         err = GetLastError();\r
7520     }\r
7521   }\r
7522   return err;\r
7523 }\r
7524 \r
7525 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7526 void CheckForInputBufferFull( InputSource * is )\r
7527 {\r
7528     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7529         /* Look for end of line */\r
7530         char * p = is->buf;\r
7531         \r
7532         while( p < is->next && *p != '\n' ) {\r
7533             p++;\r
7534         }\r
7535 \r
7536         if( p >= is->next ) {\r
7537             if (appData.debugMode) {\r
7538                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7539             }\r
7540 \r
7541             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7542             is->count = (DWORD) -1;\r
7543             is->next = is->buf;\r
7544         }\r
7545     }\r
7546 }\r
7547 \r
7548 DWORD\r
7549 InputThread(LPVOID arg)\r
7550 {\r
7551   InputSource *is;\r
7552   OVERLAPPED ovl;\r
7553 \r
7554   is = (InputSource *) arg;\r
7555   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7556   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7557   while (is->hThread != NULL) {\r
7558     is->error = DoReadFile(is->hFile, is->next,\r
7559                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7560                            &is->count, &ovl);\r
7561     if (is->error == NO_ERROR) {\r
7562       is->next += is->count;\r
7563     } else {\r
7564       if (is->error == ERROR_BROKEN_PIPE) {\r
7565         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7566         is->count = 0;\r
7567       } else {\r
7568         is->count = (DWORD) -1;\r
7569         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7570         break; \r
7571       }\r
7572     }\r
7573 \r
7574     CheckForInputBufferFull( is );\r
7575 \r
7576     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7577 \r
7578     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7579 \r
7580     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7581   }\r
7582 \r
7583   CloseHandle(ovl.hEvent);\r
7584   CloseHandle(is->hFile);\r
7585 \r
7586   if (appData.debugMode) {\r
7587     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7588   }\r
7589 \r
7590   return 0;\r
7591 }\r
7592 \r
7593 \r
7594 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7595 DWORD\r
7596 NonOvlInputThread(LPVOID arg)\r
7597 {\r
7598   InputSource *is;\r
7599   char *p, *q;\r
7600   int i;\r
7601   char prev;\r
7602 \r
7603   is = (InputSource *) arg;\r
7604   while (is->hThread != NULL) {\r
7605     is->error = ReadFile(is->hFile, is->next,\r
7606                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7607                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7608     if (is->error == NO_ERROR) {\r
7609       /* Change CRLF to LF */\r
7610       if (is->next > is->buf) {\r
7611         p = is->next - 1;\r
7612         i = is->count + 1;\r
7613       } else {\r
7614         p = is->next;\r
7615         i = is->count;\r
7616       }\r
7617       q = p;\r
7618       prev = NULLCHAR;\r
7619       while (i > 0) {\r
7620         if (prev == '\r' && *p == '\n') {\r
7621           *(q-1) = '\n';\r
7622           is->count--;\r
7623         } else { \r
7624           *q++ = *p;\r
7625         }\r
7626         prev = *p++;\r
7627         i--;\r
7628       }\r
7629       *q = NULLCHAR;\r
7630       is->next = q;\r
7631     } else {\r
7632       if (is->error == ERROR_BROKEN_PIPE) {\r
7633         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7634         is->count = 0; \r
7635       } else {\r
7636         is->count = (DWORD) -1;\r
7637       }\r
7638     }\r
7639 \r
7640     CheckForInputBufferFull( is );\r
7641 \r
7642     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7643 \r
7644     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7645 \r
7646     if (is->count < 0) break;  /* Quit on error */\r
7647   }\r
7648   CloseHandle(is->hFile);\r
7649   return 0;\r
7650 }\r
7651 \r
7652 DWORD\r
7653 SocketInputThread(LPVOID arg)\r
7654 {\r
7655   InputSource *is;\r
7656 \r
7657   is = (InputSource *) arg;\r
7658   while (is->hThread != NULL) {\r
7659     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7660     if ((int)is->count == SOCKET_ERROR) {\r
7661       is->count = (DWORD) -1;\r
7662       is->error = WSAGetLastError();\r
7663     } else {\r
7664       is->error = NO_ERROR;\r
7665       is->next += is->count;\r
7666       if (is->count == 0 && is->second == is) {\r
7667         /* End of file on stderr; quit with no message */\r
7668         break;\r
7669       }\r
7670     }\r
7671     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7672 \r
7673     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7674 \r
7675     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7676   }\r
7677   return 0;\r
7678 }\r
7679 \r
7680 VOID\r
7681 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7682 {\r
7683   InputSource *is;\r
7684 \r
7685   is = (InputSource *) lParam;\r
7686   if (is->lineByLine) {\r
7687     /* Feed in lines one by one */\r
7688     char *p = is->buf;\r
7689     char *q = p;\r
7690     while (q < is->next) {\r
7691       if (*q++ == '\n') {\r
7692         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7693         p = q;\r
7694       }\r
7695     }\r
7696     \r
7697     /* Move any partial line to the start of the buffer */\r
7698     q = is->buf;\r
7699     while (p < is->next) {\r
7700       *q++ = *p++;\r
7701     }\r
7702     is->next = q;\r
7703 \r
7704     if (is->error != NO_ERROR || is->count == 0) {\r
7705       /* Notify backend of the error.  Note: If there was a partial\r
7706          line at the end, it is not flushed through. */\r
7707       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7708     }\r
7709   } else {\r
7710     /* Feed in the whole chunk of input at once */\r
7711     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7712     is->next = is->buf;\r
7713   }\r
7714 }\r
7715 \r
7716 /*---------------------------------------------------------------------------*\\r
7717  *\r
7718  *  Menu enables. Used when setting various modes.\r
7719  *\r
7720 \*---------------------------------------------------------------------------*/\r
7721 \r
7722 typedef struct {\r
7723   int item;\r
7724   int flags;\r
7725 } Enables;\r
7726 \r
7727 VOID\r
7728 GreyRevert(Boolean grey)\r
7729 { // [HGM] vari: for retracting variations in local mode\r
7730   HMENU hmenu = GetMenu(hwndMain);\r
7731   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7732   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7733 }\r
7734 \r
7735 VOID\r
7736 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7737 {\r
7738   while (enab->item > 0) {\r
7739     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7740     enab++;\r
7741   }\r
7742 }\r
7743 \r
7744 Enables gnuEnables[] = {\r
7745   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7746   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7747   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7748   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7749   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7750   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7751   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7752   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7753   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7754   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7755   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7756   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7757   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7758 \r
7759   // Needed to switch from ncp to GNU mode on Engine Load\r
7760   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7761   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7762   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7763   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7764   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7765   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7766   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7767   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7768   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7769   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7770   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7771   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7772   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7773   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7774   { -1, -1 }\r
7775 };\r
7776 \r
7777 Enables icsEnables[] = {\r
7778   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7779   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7780   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7781   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7782   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7783   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7784   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7785   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7786   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7787   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7788   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7789   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7790   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7791   { IDM_EditProgs2, MF_BYCOMMAND|MF_GRAYED },\r
7792   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7793   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7794   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7795   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7796   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
7797   { -1, -1 }\r
7798 };\r
7799 \r
7800 #if ZIPPY\r
7801 Enables zippyEnables[] = {\r
7802   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7803   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7804   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7805   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7806   { -1, -1 }\r
7807 };\r
7808 #endif\r
7809 \r
7810 Enables ncpEnables[] = {\r
7811   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7812   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7813   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7814   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7815   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7816   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7817   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7818   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7819   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7820   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7821   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7822   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7823   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7824   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7825   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7826   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7827   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7828   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7829   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7830   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7831   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7832   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7833   { -1, -1 }\r
7834 };\r
7835 \r
7836 Enables trainingOnEnables[] = {\r
7837   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7838   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7839   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7840   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7841   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7842   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7843   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7844   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7845   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7846   { -1, -1 }\r
7847 };\r
7848 \r
7849 Enables trainingOffEnables[] = {\r
7850   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7851   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7852   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7853   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7854   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7855   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7856   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7857   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7858   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7859   { -1, -1 }\r
7860 };\r
7861 \r
7862 /* These modify either ncpEnables or gnuEnables */\r
7863 Enables cmailEnables[] = {\r
7864   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7865   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7866   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7867   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7868   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7869   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7870   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7871   { -1, -1 }\r
7872 };\r
7873 \r
7874 Enables machineThinkingEnables[] = {\r
7875   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7876   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7877   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7878   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7879   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7880   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7881   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7882   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7883   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7884   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7885   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7886   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7887   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7888 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7889   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7890   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7891   { -1, -1 }\r
7892 };\r
7893 \r
7894 Enables userThinkingEnables[] = {\r
7895   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7896   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7897   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7898   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7899   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7900   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7901   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7902   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7903   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7904   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7905   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7906   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7907   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7908 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7909   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7910   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7911   { -1, -1 }\r
7912 };\r
7913 \r
7914 /*---------------------------------------------------------------------------*\\r
7915  *\r
7916  *  Front-end interface functions exported by XBoard.\r
7917  *  Functions appear in same order as prototypes in frontend.h.\r
7918  * \r
7919 \*---------------------------------------------------------------------------*/\r
7920 VOID\r
7921 CheckMark(UINT item, int state)\r
7922 {\r
7923     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
7924 }\r
7925 \r
7926 VOID\r
7927 ModeHighlight()\r
7928 {\r
7929   static UINT prevChecked = 0;\r
7930   static int prevPausing = 0;\r
7931   UINT nowChecked;\r
7932 \r
7933   if (pausing != prevPausing) {\r
7934     prevPausing = pausing;\r
7935     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7936                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7937     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7938   }\r
7939 \r
7940   switch (gameMode) {\r
7941   case BeginningOfGame:\r
7942     if (appData.icsActive)\r
7943       nowChecked = IDM_IcsClient;\r
7944     else if (appData.noChessProgram)\r
7945       nowChecked = IDM_EditGame;\r
7946     else\r
7947       nowChecked = IDM_MachineBlack;\r
7948     break;\r
7949   case MachinePlaysBlack:\r
7950     nowChecked = IDM_MachineBlack;\r
7951     break;\r
7952   case MachinePlaysWhite:\r
7953     nowChecked = IDM_MachineWhite;\r
7954     break;\r
7955   case TwoMachinesPlay:\r
7956     nowChecked = IDM_TwoMachines;\r
7957     break;\r
7958   case AnalyzeMode:\r
7959     nowChecked = IDM_AnalysisMode;\r
7960     break;\r
7961   case AnalyzeFile:\r
7962     nowChecked = IDM_AnalyzeFile;\r
7963     break;\r
7964   case EditGame:\r
7965     nowChecked = IDM_EditGame;\r
7966     break;\r
7967   case PlayFromGameFile:\r
7968     nowChecked = IDM_LoadGame;\r
7969     break;\r
7970   case EditPosition:\r
7971     nowChecked = IDM_EditPosition;\r
7972     break;\r
7973   case Training:\r
7974     nowChecked = IDM_Training;\r
7975     break;\r
7976   case IcsPlayingWhite:\r
7977   case IcsPlayingBlack:\r
7978   case IcsObserving:\r
7979   case IcsIdle:\r
7980     nowChecked = IDM_IcsClient;\r
7981     break;\r
7982   default:\r
7983   case EndOfGame:\r
7984     nowChecked = 0;\r
7985     break;\r
7986   }\r
7987   CheckMark(prevChecked, MF_UNCHECKED);\r
7988   CheckMark(nowChecked, MF_CHECKED);\r
7989   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
7990 \r
7991   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
7992     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
7993                           MF_BYCOMMAND|MF_ENABLED);\r
7994   } else {\r
7995     (void) EnableMenuItem(GetMenu(hwndMain), \r
7996                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
7997   }\r
7998 \r
7999   prevChecked = nowChecked;\r
8000 \r
8001   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8002   if (appData.icsActive) {\r
8003        if (appData.icsEngineAnalyze) {\r
8004                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8005        } else {\r
8006                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8007        }\r
8008   }\r
8009   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8010 }\r
8011 \r
8012 VOID\r
8013 SetICSMode()\r
8014 {\r
8015   HMENU hmenu = GetMenu(hwndMain);\r
8016   SetMenuEnables(hmenu, icsEnables);\r
8017   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8018     MF_BYCOMMAND|MF_ENABLED);\r
8019 #if ZIPPY\r
8020   if (appData.zippyPlay) {\r
8021     SetMenuEnables(hmenu, zippyEnables);\r
8022     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8023          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8024           MF_BYCOMMAND|MF_ENABLED);\r
8025   }\r
8026 #endif\r
8027 }\r
8028 \r
8029 VOID\r
8030 SetGNUMode()\r
8031 {\r
8032   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8033 }\r
8034 \r
8035 VOID\r
8036 SetNCPMode()\r
8037 {\r
8038   HMENU hmenu = GetMenu(hwndMain);\r
8039   SetMenuEnables(hmenu, ncpEnables);\r
8040     DrawMenuBar(hwndMain);\r
8041 }\r
8042 \r
8043 VOID\r
8044 SetCmailMode()\r
8045 {\r
8046   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8047 }\r
8048 \r
8049 VOID \r
8050 SetTrainingModeOn()\r
8051 {\r
8052   int i;\r
8053   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8054   for (i = 0; i < N_BUTTONS; i++) {\r
8055     if (buttonDesc[i].hwnd != NULL)\r
8056       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8057   }\r
8058   CommentPopDown();\r
8059 }\r
8060 \r
8061 VOID SetTrainingModeOff()\r
8062 {\r
8063   int i;\r
8064   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8065   for (i = 0; i < N_BUTTONS; i++) {\r
8066     if (buttonDesc[i].hwnd != NULL)\r
8067       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8068   }\r
8069 }\r
8070 \r
8071 \r
8072 VOID\r
8073 SetUserThinkingEnables()\r
8074 {\r
8075   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8076 }\r
8077 \r
8078 VOID\r
8079 SetMachineThinkingEnables()\r
8080 {\r
8081   HMENU hMenu = GetMenu(hwndMain);\r
8082   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8083 \r
8084   SetMenuEnables(hMenu, machineThinkingEnables);\r
8085 \r
8086   if (gameMode == MachinePlaysBlack) {\r
8087     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8088   } else if (gameMode == MachinePlaysWhite) {\r
8089     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8090   } else if (gameMode == TwoMachinesPlay) {\r
8091     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8092   }\r
8093 }\r
8094 \r
8095 \r
8096 VOID\r
8097 DisplayTitle(char *str)\r
8098 {\r
8099   char title[MSG_SIZ], *host;\r
8100   if (str[0] != NULLCHAR) {\r
8101     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8102   } else if (appData.icsActive) {\r
8103     if (appData.icsCommPort[0] != NULLCHAR)\r
8104       host = "ICS";\r
8105     else \r
8106       host = appData.icsHost;\r
8107       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8108   } else if (appData.noChessProgram) {\r
8109     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8110   } else {\r
8111     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8112     strcat(title, ": ");\r
8113     strcat(title, first.tidy);\r
8114   }\r
8115   SetWindowText(hwndMain, title);\r
8116 }\r
8117 \r
8118 \r
8119 VOID\r
8120 DisplayMessage(char *str1, char *str2)\r
8121 {\r
8122   HDC hdc;\r
8123   HFONT oldFont;\r
8124   int remain = MESSAGE_TEXT_MAX - 1;\r
8125   int len;\r
8126 \r
8127   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8128   messageText[0] = NULLCHAR;\r
8129   if (*str1) {\r
8130     len = strlen(str1);\r
8131     if (len > remain) len = remain;\r
8132     strncpy(messageText, str1, len);\r
8133     messageText[len] = NULLCHAR;\r
8134     remain -= len;\r
8135   }\r
8136   if (*str2 && remain >= 2) {\r
8137     if (*str1) {\r
8138       strcat(messageText, "  ");\r
8139       remain -= 2;\r
8140     }\r
8141     len = strlen(str2);\r
8142     if (len > remain) len = remain;\r
8143     strncat(messageText, str2, len);\r
8144   }\r
8145   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
8146   safeStrCpy(lastMsg, messageText, MSG_SIZ);
8147 \r
8148   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8149 \r
8150   SAYMACHINEMOVE();\r
8151 \r
8152   hdc = GetDC(hwndMain);\r
8153   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8154   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8155              &messageRect, messageText, strlen(messageText), NULL);\r
8156   (void) SelectObject(hdc, oldFont);\r
8157   (void) ReleaseDC(hwndMain, hdc);\r
8158 }\r
8159 \r
8160 VOID\r
8161 DisplayError(char *str, int error)\r
8162 {\r
8163   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8164   int len;\r
8165 \r
8166   if (error == 0) {\r
8167     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8168   } else {\r
8169     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8170                         NULL, error, LANG_NEUTRAL,\r
8171                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8172     if (len > 0) {\r
8173       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8174     } else {\r
8175       ErrorMap *em = errmap;\r
8176       while (em->err != 0 && em->err != error) em++;\r
8177       if (em->err != 0) {\r
8178         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8179       } else {\r
8180         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8181       }\r
8182     }\r
8183   }\r
8184   \r
8185   ErrorPopUp(_("Error"), buf);\r
8186 }\r
8187 \r
8188 \r
8189 VOID\r
8190 DisplayMoveError(char *str)\r
8191 {\r
8192   fromX = fromY = -1;\r
8193   ClearHighlights();\r
8194   DrawPosition(FALSE, NULL);\r
8195   if (appData.popupMoveErrors) {\r
8196     ErrorPopUp(_("Error"), str);\r
8197   } else {\r
8198     DisplayMessage(str, "");\r
8199     moveErrorMessageUp = TRUE;\r
8200   }\r
8201 }\r
8202 \r
8203 VOID\r
8204 DisplayFatalError(char *str, int error, int exitStatus)\r
8205 {\r
8206   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8207   int len;\r
8208   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8209 \r
8210   if (error != 0) {\r
8211     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8212                         NULL, error, LANG_NEUTRAL,\r
8213                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8214     if (len > 0) {\r
8215       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8216     } else {\r
8217       ErrorMap *em = errmap;\r
8218       while (em->err != 0 && em->err != error) em++;\r
8219       if (em->err != 0) {\r
8220         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8221       } else {\r
8222         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8223       }\r
8224     }\r
8225     str = buf;\r
8226   }\r
8227   if (appData.debugMode) {\r
8228     fprintf(debugFP, "%s: %s\n", label, str);\r
8229   }\r
8230   if (appData.popupExitMessage) {\r
8231     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8232                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8233   }\r
8234   ExitEvent(exitStatus);\r
8235 }\r
8236 \r
8237 \r
8238 VOID\r
8239 DisplayInformation(char *str)\r
8240 {\r
8241   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8242 }\r
8243 \r
8244 \r
8245 VOID\r
8246 DisplayNote(char *str)\r
8247 {\r
8248   ErrorPopUp(_("Note"), str);\r
8249 }\r
8250 \r
8251 \r
8252 typedef struct {\r
8253   char *title, *question, *replyPrefix;\r
8254   ProcRef pr;\r
8255 } QuestionParams;\r
8256 \r
8257 LRESULT CALLBACK\r
8258 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8259 {\r
8260   static QuestionParams *qp;\r
8261   char reply[MSG_SIZ];\r
8262   int len, err;\r
8263 \r
8264   switch (message) {\r
8265   case WM_INITDIALOG:\r
8266     qp = (QuestionParams *) lParam;\r
8267     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8268     Translate(hDlg, DLG_Question);\r
8269     SetWindowText(hDlg, qp->title);\r
8270     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8271     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8272     return FALSE;\r
8273 \r
8274   case WM_COMMAND:\r
8275     switch (LOWORD(wParam)) {\r
8276     case IDOK:\r
8277       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8278       if (*reply) strcat(reply, " ");\r
8279       len = strlen(reply);\r
8280       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8281       strcat(reply, "\n");\r
8282       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8283       EndDialog(hDlg, TRUE);\r
8284       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8285       return TRUE;\r
8286     case IDCANCEL:\r
8287       EndDialog(hDlg, FALSE);\r
8288       return TRUE;\r
8289     default:\r
8290       break;\r
8291     }\r
8292     break;\r
8293   }\r
8294   return FALSE;\r
8295 }\r
8296 \r
8297 VOID\r
8298 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8299 {\r
8300     QuestionParams qp;\r
8301     FARPROC lpProc;\r
8302     \r
8303     qp.title = title;\r
8304     qp.question = question;\r
8305     qp.replyPrefix = replyPrefix;\r
8306     qp.pr = pr;\r
8307     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8308     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8309       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8310     FreeProcInstance(lpProc);\r
8311 }\r
8312 \r
8313 /* [AS] Pick FRC position */\r
8314 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8315 {\r
8316     static int * lpIndexFRC;\r
8317     BOOL index_is_ok;\r
8318     char buf[16];\r
8319 \r
8320     switch( message )\r
8321     {\r
8322     case WM_INITDIALOG:\r
8323         lpIndexFRC = (int *) lParam;\r
8324 \r
8325         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8326         Translate(hDlg, DLG_NewGameFRC);\r
8327 \r
8328         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8329         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8330         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8331         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8332 \r
8333         break;\r
8334 \r
8335     case WM_COMMAND:\r
8336         switch( LOWORD(wParam) ) {\r
8337         case IDOK:\r
8338             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8339             EndDialog( hDlg, 0 );\r
8340             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8341             return TRUE;\r
8342         case IDCANCEL:\r
8343             EndDialog( hDlg, 1 );   \r
8344             return TRUE;\r
8345         case IDC_NFG_Edit:\r
8346             if( HIWORD(wParam) == EN_CHANGE ) {\r
8347                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8348 \r
8349                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8350             }\r
8351             return TRUE;\r
8352         case IDC_NFG_Random:\r
8353           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8354             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8355             return TRUE;\r
8356         }\r
8357 \r
8358         break;\r
8359     }\r
8360 \r
8361     return FALSE;\r
8362 }\r
8363 \r
8364 int NewGameFRC()\r
8365 {\r
8366     int result;\r
8367     int index = appData.defaultFrcPosition;\r
8368     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8369 \r
8370     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8371 \r
8372     if( result == 0 ) {\r
8373         appData.defaultFrcPosition = index;\r
8374     }\r
8375 \r
8376     return result;\r
8377 }\r
8378 \r
8379 /* [AS] Game list options. Refactored by HGM */\r
8380 \r
8381 HWND gameListOptionsDialog;\r
8382 \r
8383 // low-level front-end: clear text edit / list widget\r
8384 void\r
8385 GLT_ClearList()\r
8386 {\r
8387     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8388 }\r
8389 \r
8390 // low-level front-end: clear text edit / list widget\r
8391 void\r
8392 GLT_DeSelectList()\r
8393 {\r
8394     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8395 }\r
8396 \r
8397 // low-level front-end: append line to text edit / list widget\r
8398 void\r
8399 GLT_AddToList( char *name )\r
8400 {\r
8401     if( name != 0 ) {\r
8402             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8403     }\r
8404 }\r
8405 \r
8406 // low-level front-end: get line from text edit / list widget\r
8407 Boolean\r
8408 GLT_GetFromList( int index, char *name )\r
8409 {\r
8410     if( name != 0 ) {\r
8411             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8412                 return TRUE;\r
8413     }\r
8414     return FALSE;\r
8415 }\r
8416 \r
8417 void GLT_MoveSelection( HWND hDlg, int delta )\r
8418 {\r
8419     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8420     int idx2 = idx1 + delta;\r
8421     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8422 \r
8423     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8424         char buf[128];\r
8425 \r
8426         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8427         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8428         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8429         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8430     }\r
8431 }\r
8432 \r
8433 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8434 {\r
8435     switch( message )\r
8436     {\r
8437     case WM_INITDIALOG:\r
8438         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8439         \r
8440         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8441         Translate(hDlg, DLG_GameListOptions);\r
8442 \r
8443         /* Initialize list */\r
8444         GLT_TagsToList( lpUserGLT );\r
8445 \r
8446         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8447 \r
8448         break;\r
8449 \r
8450     case WM_COMMAND:\r
8451         switch( LOWORD(wParam) ) {\r
8452         case IDOK:\r
8453             GLT_ParseList();\r
8454             EndDialog( hDlg, 0 );\r
8455             return TRUE;\r
8456         case IDCANCEL:\r
8457             EndDialog( hDlg, 1 );\r
8458             return TRUE;\r
8459 \r
8460         case IDC_GLT_Default:\r
8461             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8462             return TRUE;\r
8463 \r
8464         case IDC_GLT_Restore:\r
8465             GLT_TagsToList( appData.gameListTags );\r
8466             return TRUE;\r
8467 \r
8468         case IDC_GLT_Up:\r
8469             GLT_MoveSelection( hDlg, -1 );\r
8470             return TRUE;\r
8471 \r
8472         case IDC_GLT_Down:\r
8473             GLT_MoveSelection( hDlg, +1 );\r
8474             return TRUE;\r
8475         }\r
8476 \r
8477         break;\r
8478     }\r
8479 \r
8480     return FALSE;\r
8481 }\r
8482 \r
8483 int GameListOptions()\r
8484 {\r
8485     int result;\r
8486     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8487 \r
8488       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8489 \r
8490     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8491 \r
8492     if( result == 0 ) {\r
8493         /* [AS] Memory leak here! */\r
8494         appData.gameListTags = strdup( lpUserGLT ); \r
8495     }\r
8496 \r
8497     return result;\r
8498 }\r
8499 \r
8500 VOID\r
8501 DisplayIcsInteractionTitle(char *str)\r
8502 {\r
8503   char consoleTitle[MSG_SIZ];\r
8504 \r
8505     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8506   SetWindowText(hwndConsole, consoleTitle);\r
8507 }\r
8508 \r
8509 void\r
8510 DrawPosition(int fullRedraw, Board board)\r
8511 {\r
8512   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8513 }\r
8514 \r
8515 void NotifyFrontendLogin()\r
8516 {\r
8517         if (hwndConsole)\r
8518                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8519 }\r
8520 \r
8521 VOID\r
8522 ResetFrontEnd()\r
8523 {\r
8524   fromX = fromY = -1;\r
8525   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8526     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8527     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8528     dragInfo.lastpos = dragInfo.pos;\r
8529     dragInfo.start.x = dragInfo.start.y = -1;\r
8530     dragInfo.from = dragInfo.start;\r
8531     ReleaseCapture();\r
8532     DrawPosition(TRUE, NULL);\r
8533   }\r
8534   TagsPopDown();\r
8535 }\r
8536 \r
8537 \r
8538 VOID\r
8539 CommentPopUp(char *title, char *str)\r
8540 {\r
8541   HWND hwnd = GetActiveWindow();\r
8542   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8543   SAY(str);\r
8544   SetActiveWindow(hwnd);\r
8545 }\r
8546 \r
8547 VOID\r
8548 CommentPopDown(void)\r
8549 {\r
8550   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8551   if (commentDialog) {\r
8552     ShowWindow(commentDialog, SW_HIDE);\r
8553   }\r
8554   commentUp = FALSE;\r
8555 }\r
8556 \r
8557 VOID\r
8558 EditCommentPopUp(int index, char *title, char *str)\r
8559 {\r
8560   EitherCommentPopUp(index, title, str, TRUE);\r
8561 }\r
8562 \r
8563 \r
8564 VOID\r
8565 RingBell()\r
8566 {\r
8567   MyPlaySound(&sounds[(int)SoundMove]);\r
8568 }\r
8569 \r
8570 VOID PlayIcsWinSound()\r
8571 {\r
8572   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8573 }\r
8574 \r
8575 VOID PlayIcsLossSound()\r
8576 {\r
8577   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8578 }\r
8579 \r
8580 VOID PlayIcsDrawSound()\r
8581 {\r
8582   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8583 }\r
8584 \r
8585 VOID PlayIcsUnfinishedSound()\r
8586 {\r
8587   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8588 }\r
8589 \r
8590 VOID\r
8591 PlayAlarmSound()\r
8592 {\r
8593   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8594 }\r
8595 \r
8596 \r
8597 VOID\r
8598 EchoOn()\r
8599 {\r
8600   HWND hInput;\r
8601   consoleEcho = TRUE;\r
8602   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8603   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8604   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8605 }\r
8606 \r
8607 \r
8608 VOID\r
8609 EchoOff()\r
8610 {\r
8611   CHARFORMAT cf;\r
8612   HWND hInput;\r
8613   consoleEcho = FALSE;\r
8614   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8615   /* This works OK: set text and background both to the same color */\r
8616   cf = consoleCF;\r
8617   cf.crTextColor = COLOR_ECHOOFF;\r
8618   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8619   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8620 }\r
8621 \r
8622 /* No Raw()...? */\r
8623 \r
8624 void Colorize(ColorClass cc, int continuation)\r
8625 {\r
8626   currentColorClass = cc;\r
8627   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8628   consoleCF.crTextColor = textAttribs[cc].color;\r
8629   consoleCF.dwEffects = textAttribs[cc].effects;\r
8630   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8631 }\r
8632 \r
8633 char *\r
8634 UserName()\r
8635 {\r
8636   static char buf[MSG_SIZ];\r
8637   DWORD bufsiz = MSG_SIZ;\r
8638 \r
8639   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8640         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8641   }\r
8642   if (!GetUserName(buf, &bufsiz)) {\r
8643     /*DisplayError("Error getting user name", GetLastError());*/\r
8644     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8645   }\r
8646   return buf;\r
8647 }\r
8648 \r
8649 char *\r
8650 HostName()\r
8651 {\r
8652   static char buf[MSG_SIZ];\r
8653   DWORD bufsiz = MSG_SIZ;\r
8654 \r
8655   if (!GetComputerName(buf, &bufsiz)) {\r
8656     /*DisplayError("Error getting host name", GetLastError());*/\r
8657     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8658   }\r
8659   return buf;\r
8660 }\r
8661 \r
8662 \r
8663 int\r
8664 ClockTimerRunning()\r
8665 {\r
8666   return clockTimerEvent != 0;\r
8667 }\r
8668 \r
8669 int\r
8670 StopClockTimer()\r
8671 {\r
8672   if (clockTimerEvent == 0) return FALSE;\r
8673   KillTimer(hwndMain, clockTimerEvent);\r
8674   clockTimerEvent = 0;\r
8675   return TRUE;\r
8676 }\r
8677 \r
8678 void\r
8679 StartClockTimer(long millisec)\r
8680 {\r
8681   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8682                              (UINT) millisec, NULL);\r
8683 }\r
8684 \r
8685 void\r
8686 DisplayWhiteClock(long timeRemaining, int highlight)\r
8687 {\r
8688   HDC hdc;\r
8689   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8690 \r
8691   if(appData.noGUI) return;\r
8692   hdc = GetDC(hwndMain);\r
8693   if (!IsIconic(hwndMain)) {\r
8694     DisplayAClock(hdc, timeRemaining, highlight, \r
8695                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8696   }\r
8697   if (highlight && iconCurrent == iconBlack) {\r
8698     iconCurrent = iconWhite;\r
8699     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8700     if (IsIconic(hwndMain)) {\r
8701       DrawIcon(hdc, 2, 2, iconCurrent);\r
8702     }\r
8703   }\r
8704   (void) ReleaseDC(hwndMain, hdc);\r
8705   if (hwndConsole)\r
8706     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8707 }\r
8708 \r
8709 void\r
8710 DisplayBlackClock(long timeRemaining, int highlight)\r
8711 {\r
8712   HDC hdc;\r
8713   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8714 \r
8715   if(appData.noGUI) return;\r
8716   hdc = GetDC(hwndMain);\r
8717   if (!IsIconic(hwndMain)) {\r
8718     DisplayAClock(hdc, timeRemaining, highlight, \r
8719                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8720   }\r
8721   if (highlight && iconCurrent == iconWhite) {\r
8722     iconCurrent = iconBlack;\r
8723     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8724     if (IsIconic(hwndMain)) {\r
8725       DrawIcon(hdc, 2, 2, iconCurrent);\r
8726     }\r
8727   }\r
8728   (void) ReleaseDC(hwndMain, hdc);\r
8729   if (hwndConsole)\r
8730     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8731 }\r
8732 \r
8733 \r
8734 int\r
8735 LoadGameTimerRunning()\r
8736 {\r
8737   return loadGameTimerEvent != 0;\r
8738 }\r
8739 \r
8740 int\r
8741 StopLoadGameTimer()\r
8742 {\r
8743   if (loadGameTimerEvent == 0) return FALSE;\r
8744   KillTimer(hwndMain, loadGameTimerEvent);\r
8745   loadGameTimerEvent = 0;\r
8746   return TRUE;\r
8747 }\r
8748 \r
8749 void\r
8750 StartLoadGameTimer(long millisec)\r
8751 {\r
8752   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8753                                 (UINT) millisec, NULL);\r
8754 }\r
8755 \r
8756 void\r
8757 AutoSaveGame()\r
8758 {\r
8759   char *defName;\r
8760   FILE *f;\r
8761   char fileTitle[MSG_SIZ];\r
8762 \r
8763   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8764   f = OpenFileDialog(hwndMain, "a", defName,\r
8765                      appData.oldSaveStyle ? "gam" : "pgn",\r
8766                      GAME_FILT, \r
8767                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8768   if (f != NULL) {\r
8769     SaveGame(f, 0, "");\r
8770     fclose(f);\r
8771   }\r
8772 }\r
8773 \r
8774 \r
8775 void\r
8776 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8777 {\r
8778   if (delayedTimerEvent != 0) {\r
8779     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8780       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8781     }\r
8782     KillTimer(hwndMain, delayedTimerEvent);\r
8783     delayedTimerEvent = 0;\r
8784     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8785     delayedTimerCallback();\r
8786   }\r
8787   delayedTimerCallback = cb;\r
8788   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8789                                 (UINT) millisec, NULL);\r
8790 }\r
8791 \r
8792 DelayedEventCallback\r
8793 GetDelayedEvent()\r
8794 {\r
8795   if (delayedTimerEvent) {\r
8796     return delayedTimerCallback;\r
8797   } else {\r
8798     return NULL;\r
8799   }\r
8800 }\r
8801 \r
8802 void\r
8803 CancelDelayedEvent()\r
8804 {\r
8805   if (delayedTimerEvent) {\r
8806     KillTimer(hwndMain, delayedTimerEvent);\r
8807     delayedTimerEvent = 0;\r
8808   }\r
8809 }\r
8810 \r
8811 DWORD GetWin32Priority(int nice)\r
8812 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8813 /*\r
8814 REALTIME_PRIORITY_CLASS     0x00000100\r
8815 HIGH_PRIORITY_CLASS         0x00000080\r
8816 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8817 NORMAL_PRIORITY_CLASS       0x00000020\r
8818 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8819 IDLE_PRIORITY_CLASS         0x00000040\r
8820 */\r
8821         if (nice < -15) return 0x00000080;\r
8822         if (nice < 0)   return 0x00008000;\r
8823         if (nice == 0)  return 0x00000020;\r
8824         if (nice < 15)  return 0x00004000;\r
8825         return 0x00000040;\r
8826 }\r
8827 \r
8828 /* Start a child process running the given program.\r
8829    The process's standard output can be read from "from", and its\r
8830    standard input can be written to "to".\r
8831    Exit with fatal error if anything goes wrong.\r
8832    Returns an opaque pointer that can be used to destroy the process\r
8833    later.\r
8834 */\r
8835 int\r
8836 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8837 {\r
8838 #define BUFSIZE 4096\r
8839 \r
8840   HANDLE hChildStdinRd, hChildStdinWr,\r
8841     hChildStdoutRd, hChildStdoutWr;\r
8842   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8843   SECURITY_ATTRIBUTES saAttr;\r
8844   BOOL fSuccess;\r
8845   PROCESS_INFORMATION piProcInfo;\r
8846   STARTUPINFO siStartInfo;\r
8847   ChildProc *cp;\r
8848   char buf[MSG_SIZ];\r
8849   DWORD err;\r
8850 \r
8851   if (appData.debugMode) {\r
8852     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8853   }\r
8854 \r
8855   *pr = NoProc;\r
8856 \r
8857   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8858   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8859   saAttr.bInheritHandle = TRUE;\r
8860   saAttr.lpSecurityDescriptor = NULL;\r
8861 \r
8862   /*\r
8863    * The steps for redirecting child's STDOUT:\r
8864    *     1. Create anonymous pipe to be STDOUT for child.\r
8865    *     2. Create a noninheritable duplicate of read handle,\r
8866    *         and close the inheritable read handle.\r
8867    */\r
8868 \r
8869   /* Create a pipe for the child's STDOUT. */\r
8870   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8871     return GetLastError();\r
8872   }\r
8873 \r
8874   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8875   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8876                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8877                              FALSE,     /* not inherited */\r
8878                              DUPLICATE_SAME_ACCESS);\r
8879   if (! fSuccess) {\r
8880     return GetLastError();\r
8881   }\r
8882   CloseHandle(hChildStdoutRd);\r
8883 \r
8884   /*\r
8885    * The steps for redirecting child's STDIN:\r
8886    *     1. Create anonymous pipe to be STDIN for child.\r
8887    *     2. Create a noninheritable duplicate of write handle,\r
8888    *         and close the inheritable write handle.\r
8889    */\r
8890 \r
8891   /* Create a pipe for the child's STDIN. */\r
8892   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8893     return GetLastError();\r
8894   }\r
8895 \r
8896   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8897   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8898                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8899                              FALSE,     /* not inherited */\r
8900                              DUPLICATE_SAME_ACCESS);\r
8901   if (! fSuccess) {\r
8902     return GetLastError();\r
8903   }\r
8904   CloseHandle(hChildStdinWr);\r
8905 \r
8906   /* Arrange to (1) look in dir for the child .exe file, and\r
8907    * (2) have dir be the child's working directory.  Interpret\r
8908    * dir relative to the directory WinBoard loaded from. */\r
8909   GetCurrentDirectory(MSG_SIZ, buf);\r
8910   SetCurrentDirectory(installDir);\r
8911   // kludgey way to update logos in tourney, as long as back-end can't do it\r
8912   if(!strcmp(cmdLine, first.program)) LoadLogo(&first, 0); else\r
8913   if(!strcmp(cmdLine, second.program)) LoadLogo(&second, 1);\r
8914   SetCurrentDirectory(dir);\r
8915 \r
8916   /* Now create the child process. */\r
8917 \r
8918   siStartInfo.cb = sizeof(STARTUPINFO);\r
8919   siStartInfo.lpReserved = NULL;\r
8920   siStartInfo.lpDesktop = NULL;\r
8921   siStartInfo.lpTitle = NULL;\r
8922   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8923   siStartInfo.cbReserved2 = 0;\r
8924   siStartInfo.lpReserved2 = NULL;\r
8925   siStartInfo.hStdInput = hChildStdinRd;\r
8926   siStartInfo.hStdOutput = hChildStdoutWr;\r
8927   siStartInfo.hStdError = hChildStdoutWr;\r
8928 \r
8929   fSuccess = CreateProcess(NULL,\r
8930                            cmdLine,        /* command line */\r
8931                            NULL,           /* process security attributes */\r
8932                            NULL,           /* primary thread security attrs */\r
8933                            TRUE,           /* handles are inherited */\r
8934                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8935                            NULL,           /* use parent's environment */\r
8936                            NULL,\r
8937                            &siStartInfo, /* STARTUPINFO pointer */\r
8938                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
8939 \r
8940   err = GetLastError();\r
8941   SetCurrentDirectory(buf); /* return to prev directory */\r
8942   if (! fSuccess) {\r
8943     return err;\r
8944   }\r
8945 \r
8946   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
8947     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
8948     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
8949   }\r
8950 \r
8951   /* Close the handles we don't need in the parent */\r
8952   CloseHandle(piProcInfo.hThread);\r
8953   CloseHandle(hChildStdinRd);\r
8954   CloseHandle(hChildStdoutWr);\r
8955 \r
8956   /* Prepare return value */\r
8957   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8958   cp->kind = CPReal;\r
8959   cp->hProcess = piProcInfo.hProcess;\r
8960   cp->pid = piProcInfo.dwProcessId;\r
8961   cp->hFrom = hChildStdoutRdDup;\r
8962   cp->hTo = hChildStdinWrDup;\r
8963 \r
8964   *pr = (void *) cp;\r
8965 \r
8966   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
8967      2000 where engines sometimes don't see the initial command(s)\r
8968      from WinBoard and hang.  I don't understand how that can happen,\r
8969      but the Sleep is harmless, so I've put it in.  Others have also\r
8970      reported what may be the same problem, so hopefully this will fix\r
8971      it for them too.  */\r
8972   Sleep(500);\r
8973 \r
8974   return NO_ERROR;\r
8975 }\r
8976 \r
8977 \r
8978 void\r
8979 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
8980 {\r
8981   ChildProc *cp; int result;\r
8982 \r
8983   cp = (ChildProc *) pr;\r
8984   if (cp == NULL) return;\r
8985 \r
8986   switch (cp->kind) {\r
8987   case CPReal:\r
8988     /* TerminateProcess is considered harmful, so... */\r
8989     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
8990     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
8991     /* The following doesn't work because the chess program\r
8992        doesn't "have the same console" as WinBoard.  Maybe\r
8993        we could arrange for this even though neither WinBoard\r
8994        nor the chess program uses a console for stdio? */\r
8995     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
8996 \r
8997     /* [AS] Special termination modes for misbehaving programs... */\r
8998     if( signal == 9 ) { \r
8999         result = TerminateProcess( cp->hProcess, 0 );\r
9000 \r
9001         if ( appData.debugMode) {\r
9002             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9003         }\r
9004     }\r
9005     else if( signal == 10 ) {\r
9006         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9007 \r
9008         if( dw != WAIT_OBJECT_0 ) {\r
9009             result = TerminateProcess( cp->hProcess, 0 );\r
9010 \r
9011             if ( appData.debugMode) {\r
9012                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9013             }\r
9014 \r
9015         }\r
9016     }\r
9017 \r
9018     CloseHandle(cp->hProcess);\r
9019     break;\r
9020 \r
9021   case CPComm:\r
9022     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9023     break;\r
9024 \r
9025   case CPSock:\r
9026     closesocket(cp->sock);\r
9027     WSACleanup();\r
9028     break;\r
9029 \r
9030   case CPRcmd:\r
9031     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9032     closesocket(cp->sock);\r
9033     closesocket(cp->sock2);\r
9034     WSACleanup();\r
9035     break;\r
9036   }\r
9037   free(cp);\r
9038 }\r
9039 \r
9040 void\r
9041 InterruptChildProcess(ProcRef pr)\r
9042 {\r
9043   ChildProc *cp;\r
9044 \r
9045   cp = (ChildProc *) pr;\r
9046   if (cp == NULL) return;\r
9047   switch (cp->kind) {\r
9048   case CPReal:\r
9049     /* The following doesn't work because the chess program\r
9050        doesn't "have the same console" as WinBoard.  Maybe\r
9051        we could arrange for this even though neither WinBoard\r
9052        nor the chess program uses a console for stdio */\r
9053     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9054     break;\r
9055 \r
9056   case CPComm:\r
9057   case CPSock:\r
9058     /* Can't interrupt */\r
9059     break;\r
9060 \r
9061   case CPRcmd:\r
9062     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9063     break;\r
9064   }\r
9065 }\r
9066 \r
9067 \r
9068 int\r
9069 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9070 {\r
9071   char cmdLine[MSG_SIZ];\r
9072 \r
9073   if (port[0] == NULLCHAR) {\r
9074     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9075   } else {\r
9076     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9077   }\r
9078   return StartChildProcess(cmdLine, "", pr);\r
9079 }\r
9080 \r
9081 \r
9082 /* Code to open TCP sockets */\r
9083 \r
9084 int\r
9085 OpenTCP(char *host, char *port, ProcRef *pr)\r
9086 {\r
9087   ChildProc *cp;\r
9088   int err;\r
9089   SOCKET s;\r
9090   struct sockaddr_in sa, mysa;\r
9091   struct hostent FAR *hp;\r
9092   unsigned short uport;\r
9093   WORD wVersionRequested;\r
9094   WSADATA wsaData;\r
9095 \r
9096   /* Initialize socket DLL */\r
9097   wVersionRequested = MAKEWORD(1, 1);\r
9098   err = WSAStartup(wVersionRequested, &wsaData);\r
9099   if (err != 0) return err;\r
9100 \r
9101   /* Make socket */\r
9102   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9103     err = WSAGetLastError();\r
9104     WSACleanup();\r
9105     return err;\r
9106   }\r
9107 \r
9108   /* Bind local address using (mostly) don't-care values.\r
9109    */\r
9110   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9111   mysa.sin_family = AF_INET;\r
9112   mysa.sin_addr.s_addr = INADDR_ANY;\r
9113   uport = (unsigned short) 0;\r
9114   mysa.sin_port = htons(uport);\r
9115   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9116       == SOCKET_ERROR) {\r
9117     err = WSAGetLastError();\r
9118     WSACleanup();\r
9119     return err;\r
9120   }\r
9121 \r
9122   /* Resolve remote host name */\r
9123   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9124   if (!(hp = gethostbyname(host))) {\r
9125     unsigned int b0, b1, b2, b3;\r
9126 \r
9127     err = WSAGetLastError();\r
9128 \r
9129     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9130       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9131       hp->h_addrtype = AF_INET;\r
9132       hp->h_length = 4;\r
9133       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9134       hp->h_addr_list[0] = (char *) malloc(4);\r
9135       hp->h_addr_list[0][0] = (char) b0;\r
9136       hp->h_addr_list[0][1] = (char) b1;\r
9137       hp->h_addr_list[0][2] = (char) b2;\r
9138       hp->h_addr_list[0][3] = (char) b3;\r
9139     } else {\r
9140       WSACleanup();\r
9141       return err;\r
9142     }\r
9143   }\r
9144   sa.sin_family = hp->h_addrtype;\r
9145   uport = (unsigned short) atoi(port);\r
9146   sa.sin_port = htons(uport);\r
9147   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9148 \r
9149   /* Make connection */\r
9150   if (connect(s, (struct sockaddr *) &sa,\r
9151               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9152     err = WSAGetLastError();\r
9153     WSACleanup();\r
9154     return err;\r
9155   }\r
9156 \r
9157   /* Prepare return value */\r
9158   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9159   cp->kind = CPSock;\r
9160   cp->sock = s;\r
9161   *pr = (ProcRef *) cp;\r
9162 \r
9163   return NO_ERROR;\r
9164 }\r
9165 \r
9166 int\r
9167 OpenCommPort(char *name, ProcRef *pr)\r
9168 {\r
9169   HANDLE h;\r
9170   COMMTIMEOUTS ct;\r
9171   ChildProc *cp;\r
9172   char fullname[MSG_SIZ];\r
9173 \r
9174   if (*name != '\\')\r
9175     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9176   else\r
9177     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9178 \r
9179   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9180                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9181   if (h == (HANDLE) -1) {\r
9182     return GetLastError();\r
9183   }\r
9184   hCommPort = h;\r
9185 \r
9186   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9187 \r
9188   /* Accumulate characters until a 100ms pause, then parse */\r
9189   ct.ReadIntervalTimeout = 100;\r
9190   ct.ReadTotalTimeoutMultiplier = 0;\r
9191   ct.ReadTotalTimeoutConstant = 0;\r
9192   ct.WriteTotalTimeoutMultiplier = 0;\r
9193   ct.WriteTotalTimeoutConstant = 0;\r
9194   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9195 \r
9196   /* Prepare return value */\r
9197   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9198   cp->kind = CPComm;\r
9199   cp->hFrom = h;\r
9200   cp->hTo = h;\r
9201   *pr = (ProcRef *) cp;\r
9202 \r
9203   return NO_ERROR;\r
9204 }\r
9205 \r
9206 int\r
9207 OpenLoopback(ProcRef *pr)\r
9208 {\r
9209   DisplayFatalError(_("Not implemented"), 0, 1);\r
9210   return NO_ERROR;\r
9211 }\r
9212 \r
9213 \r
9214 int\r
9215 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9216 {\r
9217   ChildProc *cp;\r
9218   int err;\r
9219   SOCKET s, s2, s3;\r
9220   struct sockaddr_in sa, mysa;\r
9221   struct hostent FAR *hp;\r
9222   unsigned short uport;\r
9223   WORD wVersionRequested;\r
9224   WSADATA wsaData;\r
9225   int fromPort;\r
9226   char stderrPortStr[MSG_SIZ];\r
9227 \r
9228   /* Initialize socket DLL */\r
9229   wVersionRequested = MAKEWORD(1, 1);\r
9230   err = WSAStartup(wVersionRequested, &wsaData);\r
9231   if (err != 0) return err;\r
9232 \r
9233   /* Resolve remote host name */\r
9234   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9235   if (!(hp = gethostbyname(host))) {\r
9236     unsigned int b0, b1, b2, b3;\r
9237 \r
9238     err = WSAGetLastError();\r
9239 \r
9240     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9241       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9242       hp->h_addrtype = AF_INET;\r
9243       hp->h_length = 4;\r
9244       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9245       hp->h_addr_list[0] = (char *) malloc(4);\r
9246       hp->h_addr_list[0][0] = (char) b0;\r
9247       hp->h_addr_list[0][1] = (char) b1;\r
9248       hp->h_addr_list[0][2] = (char) b2;\r
9249       hp->h_addr_list[0][3] = (char) b3;\r
9250     } else {\r
9251       WSACleanup();\r
9252       return err;\r
9253     }\r
9254   }\r
9255   sa.sin_family = hp->h_addrtype;\r
9256   uport = (unsigned short) 514;\r
9257   sa.sin_port = htons(uport);\r
9258   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9259 \r
9260   /* Bind local socket to unused "privileged" port address\r
9261    */\r
9262   s = INVALID_SOCKET;\r
9263   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9264   mysa.sin_family = AF_INET;\r
9265   mysa.sin_addr.s_addr = INADDR_ANY;\r
9266   for (fromPort = 1023;; fromPort--) {\r
9267     if (fromPort < 0) {\r
9268       WSACleanup();\r
9269       return WSAEADDRINUSE;\r
9270     }\r
9271     if (s == INVALID_SOCKET) {\r
9272       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9273         err = WSAGetLastError();\r
9274         WSACleanup();\r
9275         return err;\r
9276       }\r
9277     }\r
9278     uport = (unsigned short) fromPort;\r
9279     mysa.sin_port = htons(uport);\r
9280     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9281         == SOCKET_ERROR) {\r
9282       err = WSAGetLastError();\r
9283       if (err == WSAEADDRINUSE) continue;\r
9284       WSACleanup();\r
9285       return err;\r
9286     }\r
9287     if (connect(s, (struct sockaddr *) &sa,\r
9288       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9289       err = WSAGetLastError();\r
9290       if (err == WSAEADDRINUSE) {\r
9291         closesocket(s);\r
9292         s = -1;\r
9293         continue;\r
9294       }\r
9295       WSACleanup();\r
9296       return err;\r
9297     }\r
9298     break;\r
9299   }\r
9300 \r
9301   /* Bind stderr local socket to unused "privileged" port address\r
9302    */\r
9303   s2 = INVALID_SOCKET;\r
9304   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9305   mysa.sin_family = AF_INET;\r
9306   mysa.sin_addr.s_addr = INADDR_ANY;\r
9307   for (fromPort = 1023;; fromPort--) {\r
9308     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9309     if (fromPort < 0) {\r
9310       (void) closesocket(s);\r
9311       WSACleanup();\r
9312       return WSAEADDRINUSE;\r
9313     }\r
9314     if (s2 == INVALID_SOCKET) {\r
9315       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9316         err = WSAGetLastError();\r
9317         closesocket(s);\r
9318         WSACleanup();\r
9319         return err;\r
9320       }\r
9321     }\r
9322     uport = (unsigned short) fromPort;\r
9323     mysa.sin_port = htons(uport);\r
9324     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9325         == SOCKET_ERROR) {\r
9326       err = WSAGetLastError();\r
9327       if (err == WSAEADDRINUSE) continue;\r
9328       (void) closesocket(s);\r
9329       WSACleanup();\r
9330       return err;\r
9331     }\r
9332     if (listen(s2, 1) == SOCKET_ERROR) {\r
9333       err = WSAGetLastError();\r
9334       if (err == WSAEADDRINUSE) {\r
9335         closesocket(s2);\r
9336         s2 = INVALID_SOCKET;\r
9337         continue;\r
9338       }\r
9339       (void) closesocket(s);\r
9340       (void) closesocket(s2);\r
9341       WSACleanup();\r
9342       return err;\r
9343     }\r
9344     break;\r
9345   }\r
9346   prevStderrPort = fromPort; // remember port used\r
9347   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9348 \r
9349   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9350     err = WSAGetLastError();\r
9351     (void) closesocket(s);\r
9352     (void) closesocket(s2);\r
9353     WSACleanup();\r
9354     return err;\r
9355   }\r
9356 \r
9357   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9358     err = WSAGetLastError();\r
9359     (void) closesocket(s);\r
9360     (void) closesocket(s2);\r
9361     WSACleanup();\r
9362     return err;\r
9363   }\r
9364   if (*user == NULLCHAR) user = UserName();\r
9365   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9366     err = WSAGetLastError();\r
9367     (void) closesocket(s);\r
9368     (void) closesocket(s2);\r
9369     WSACleanup();\r
9370     return err;\r
9371   }\r
9372   if (send(s, cmd, strlen(cmd) + 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 \r
9380   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\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   (void) closesocket(s2);  /* Stop listening */\r
9388 \r
9389   /* Prepare return value */\r
9390   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9391   cp->kind = CPRcmd;\r
9392   cp->sock = s;\r
9393   cp->sock2 = s3;\r
9394   *pr = (ProcRef *) cp;\r
9395 \r
9396   return NO_ERROR;\r
9397 }\r
9398 \r
9399 \r
9400 InputSourceRef\r
9401 AddInputSource(ProcRef pr, int lineByLine,\r
9402                InputCallback func, VOIDSTAR closure)\r
9403 {\r
9404   InputSource *is, *is2 = NULL;\r
9405   ChildProc *cp = (ChildProc *) pr;\r
9406 \r
9407   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9408   is->lineByLine = lineByLine;\r
9409   is->func = func;\r
9410   is->closure = closure;\r
9411   is->second = NULL;\r
9412   is->next = is->buf;\r
9413   if (pr == NoProc) {\r
9414     is->kind = CPReal;\r
9415     consoleInputSource = is;\r
9416   } else {\r
9417     is->kind = cp->kind;\r
9418     /* \r
9419         [AS] Try to avoid a race condition if the thread is given control too early:\r
9420         we create all threads suspended so that the is->hThread variable can be\r
9421         safely assigned, then let the threads start with ResumeThread.\r
9422     */\r
9423     switch (cp->kind) {\r
9424     case CPReal:\r
9425       is->hFile = cp->hFrom;\r
9426       cp->hFrom = NULL; /* now owned by InputThread */\r
9427       is->hThread =\r
9428         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9429                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9430       break;\r
9431 \r
9432     case CPComm:\r
9433       is->hFile = cp->hFrom;\r
9434       cp->hFrom = NULL; /* now owned by InputThread */\r
9435       is->hThread =\r
9436         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9437                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9438       break;\r
9439 \r
9440     case CPSock:\r
9441       is->sock = cp->sock;\r
9442       is->hThread =\r
9443         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9444                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9445       break;\r
9446 \r
9447     case CPRcmd:\r
9448       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9449       *is2 = *is;\r
9450       is->sock = cp->sock;\r
9451       is->second = is2;\r
9452       is2->sock = cp->sock2;\r
9453       is2->second = is2;\r
9454       is->hThread =\r
9455         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9456                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9457       is2->hThread =\r
9458         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9459                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9460       break;\r
9461     }\r
9462 \r
9463     if( is->hThread != NULL ) {\r
9464         ResumeThread( is->hThread );\r
9465     }\r
9466 \r
9467     if( is2 != NULL && is2->hThread != NULL ) {\r
9468         ResumeThread( is2->hThread );\r
9469     }\r
9470   }\r
9471 \r
9472   return (InputSourceRef) is;\r
9473 }\r
9474 \r
9475 void\r
9476 RemoveInputSource(InputSourceRef isr)\r
9477 {\r
9478   InputSource *is;\r
9479 \r
9480   is = (InputSource *) isr;\r
9481   is->hThread = NULL;  /* tell thread to stop */\r
9482   CloseHandle(is->hThread);\r
9483   if (is->second != NULL) {\r
9484     is->second->hThread = NULL;\r
9485     CloseHandle(is->second->hThread);\r
9486   }\r
9487 }\r
9488 \r
9489 int no_wrap(char *message, int count)\r
9490 {\r
9491     ConsoleOutput(message, count, FALSE);\r
9492     return count;\r
9493 }\r
9494 \r
9495 int\r
9496 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9497 {\r
9498   DWORD dOutCount;\r
9499   int outCount = SOCKET_ERROR;\r
9500   ChildProc *cp = (ChildProc *) pr;\r
9501   static OVERLAPPED ovl;\r
9502   static int line = 0;\r
9503 \r
9504   if (pr == NoProc)\r
9505   {\r
9506     if (appData.noJoin || !appData.useInternalWrap)\r
9507       return no_wrap(message, count);\r
9508     else\r
9509     {\r
9510       int width = get_term_width();\r
9511       int len = wrap(NULL, message, count, width, &line);\r
9512       char *msg = malloc(len);\r
9513       int dbgchk;\r
9514 \r
9515       if (!msg)\r
9516         return no_wrap(message, count);\r
9517       else\r
9518       {\r
9519         dbgchk = wrap(msg, message, count, width, &line);\r
9520         if (dbgchk != len && appData.debugMode)\r
9521             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9522         ConsoleOutput(msg, len, FALSE);\r
9523         free(msg);\r
9524         return len;\r
9525       }\r
9526     }\r
9527   }\r
9528 \r
9529   if (ovl.hEvent == NULL) {\r
9530     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9531   }\r
9532   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9533 \r
9534   switch (cp->kind) {\r
9535   case CPSock:\r
9536   case CPRcmd:\r
9537     outCount = send(cp->sock, message, count, 0);\r
9538     if (outCount == SOCKET_ERROR) {\r
9539       *outError = WSAGetLastError();\r
9540     } else {\r
9541       *outError = NO_ERROR;\r
9542     }\r
9543     break;\r
9544 \r
9545   case CPReal:\r
9546     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9547                   &dOutCount, NULL)) {\r
9548       *outError = NO_ERROR;\r
9549       outCount = (int) dOutCount;\r
9550     } else {\r
9551       *outError = GetLastError();\r
9552     }\r
9553     break;\r
9554 \r
9555   case CPComm:\r
9556     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9557                             &dOutCount, &ovl);\r
9558     if (*outError == NO_ERROR) {\r
9559       outCount = (int) dOutCount;\r
9560     }\r
9561     break;\r
9562   }\r
9563   return outCount;\r
9564 }\r
9565 \r
9566 int\r
9567 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9568                        long msdelay)\r
9569 {\r
9570   /* Ignore delay, not implemented for WinBoard */\r
9571   return OutputToProcess(pr, message, count, outError);\r
9572 }\r
9573 \r
9574 \r
9575 void\r
9576 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9577                         char *buf, int count, int error)\r
9578 {\r
9579   DisplayFatalError(_("Not implemented"), 0, 1);\r
9580 }\r
9581 \r
9582 /* see wgamelist.c for Game List functions */\r
9583 /* see wedittags.c for Edit Tags functions */\r
9584 \r
9585 \r
9586 VOID\r
9587 ICSInitScript()\r
9588 {\r
9589   FILE *f;\r
9590   char buf[MSG_SIZ];\r
9591   char *dummy;\r
9592 \r
9593   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9594     f = fopen(buf, "r");\r
9595     if (f != NULL) {\r
9596       ProcessICSInitScript(f);\r
9597       fclose(f);\r
9598     }\r
9599   }\r
9600 }\r
9601 \r
9602 \r
9603 VOID\r
9604 StartAnalysisClock()\r
9605 {\r
9606   if (analysisTimerEvent) return;\r
9607   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9608                                         (UINT) 2000, NULL);\r
9609 }\r
9610 \r
9611 VOID\r
9612 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9613 {\r
9614   highlightInfo.sq[0].x = fromX;\r
9615   highlightInfo.sq[0].y = fromY;\r
9616   highlightInfo.sq[1].x = toX;\r
9617   highlightInfo.sq[1].y = toY;\r
9618 }\r
9619 \r
9620 VOID\r
9621 ClearHighlights()\r
9622 {\r
9623   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9624     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9625 }\r
9626 \r
9627 VOID\r
9628 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9629 {\r
9630   premoveHighlightInfo.sq[0].x = fromX;\r
9631   premoveHighlightInfo.sq[0].y = fromY;\r
9632   premoveHighlightInfo.sq[1].x = toX;\r
9633   premoveHighlightInfo.sq[1].y = toY;\r
9634 }\r
9635 \r
9636 VOID\r
9637 ClearPremoveHighlights()\r
9638 {\r
9639   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9640     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9641 }\r
9642 \r
9643 VOID\r
9644 ShutDownFrontEnd()\r
9645 {\r
9646   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9647   DeleteClipboardTempFiles();\r
9648 }\r
9649 \r
9650 void\r
9651 BoardToTop()\r
9652 {\r
9653     if (IsIconic(hwndMain))\r
9654       ShowWindow(hwndMain, SW_RESTORE);\r
9655 \r
9656     SetActiveWindow(hwndMain);\r
9657 }\r
9658 \r
9659 /*\r
9660  * Prototypes for animation support routines\r
9661  */\r
9662 static void ScreenSquare(int column, int row, POINT * pt);\r
9663 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9664      POINT frames[], int * nFrames);\r
9665 \r
9666 \r
9667 #define kFactor 4\r
9668 \r
9669 void\r
9670 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9671 {       // [HGM] atomic: animate blast wave\r
9672         int i;\r
9673 \r
9674         explodeInfo.fromX = fromX;\r
9675         explodeInfo.fromY = fromY;\r
9676         explodeInfo.toX = toX;\r
9677         explodeInfo.toY = toY;\r
9678         for(i=1; i<4*kFactor; i++) {\r
9679             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9680             DrawPosition(FALSE, board);\r
9681             Sleep(appData.animSpeed);\r
9682         }\r
9683         explodeInfo.radius = 0;\r
9684         DrawPosition(TRUE, board);\r
9685 }\r
9686 \r
9687 void\r
9688 AnimateMove(board, fromX, fromY, toX, toY)\r
9689      Board board;\r
9690      int fromX;\r
9691      int fromY;\r
9692      int toX;\r
9693      int toY;\r
9694 {\r
9695   ChessSquare piece;\r
9696   POINT start, finish, mid;\r
9697   POINT frames[kFactor * 2 + 1];\r
9698   int nFrames, n;\r
9699 \r
9700   if (!appData.animate) return;\r
9701   if (doingSizing) return;\r
9702   if (fromY < 0 || fromX < 0) return;\r
9703   piece = board[fromY][fromX];\r
9704   if (piece >= EmptySquare) return;\r
9705 \r
9706   ScreenSquare(fromX, fromY, &start);\r
9707   ScreenSquare(toX, toY, &finish);\r
9708 \r
9709   /* All moves except knight jumps move in straight line */\r
9710   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9711     mid.x = start.x + (finish.x - start.x) / 2;\r
9712     mid.y = start.y + (finish.y - start.y) / 2;\r
9713   } else {\r
9714     /* Knight: make straight movement then diagonal */\r
9715     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9716        mid.x = start.x + (finish.x - start.x) / 2;\r
9717        mid.y = start.y;\r
9718      } else {\r
9719        mid.x = start.x;\r
9720        mid.y = start.y + (finish.y - start.y) / 2;\r
9721      }\r
9722   }\r
9723   \r
9724   /* Don't use as many frames for very short moves */\r
9725   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9726     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9727   else\r
9728     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9729 \r
9730   animInfo.from.x = fromX;\r
9731   animInfo.from.y = fromY;\r
9732   animInfo.to.x = toX;\r
9733   animInfo.to.y = toY;\r
9734   animInfo.lastpos = start;\r
9735   animInfo.piece = piece;\r
9736   for (n = 0; n < nFrames; n++) {\r
9737     animInfo.pos = frames[n];\r
9738     DrawPosition(FALSE, NULL);\r
9739     animInfo.lastpos = animInfo.pos;\r
9740     Sleep(appData.animSpeed);\r
9741   }\r
9742   animInfo.pos = finish;\r
9743   DrawPosition(FALSE, NULL);\r
9744   animInfo.piece = EmptySquare;\r
9745   Explode(board, fromX, fromY, toX, toY);\r
9746 }\r
9747 \r
9748 /*      Convert board position to corner of screen rect and color       */\r
9749 \r
9750 static void\r
9751 ScreenSquare(column, row, pt)\r
9752      int column; int row; POINT * pt;\r
9753 {\r
9754   if (flipView) {\r
9755     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9756     pt->y = lineGap + row * (squareSize + lineGap);\r
9757   } else {\r
9758     pt->x = lineGap + column * (squareSize + lineGap);\r
9759     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9760   }\r
9761 }\r
9762 \r
9763 /*      Generate a series of frame coords from start->mid->finish.\r
9764         The movement rate doubles until the half way point is\r
9765         reached, then halves back down to the final destination,\r
9766         which gives a nice slow in/out effect. The algorithmn\r
9767         may seem to generate too many intermediates for short\r
9768         moves, but remember that the purpose is to attract the\r
9769         viewers attention to the piece about to be moved and\r
9770         then to where it ends up. Too few frames would be less\r
9771         noticeable.                                             */\r
9772 \r
9773 static void\r
9774 Tween(start, mid, finish, factor, frames, nFrames)\r
9775      POINT * start; POINT * mid;\r
9776      POINT * finish; int factor;\r
9777      POINT frames[]; int * nFrames;\r
9778 {\r
9779   int n, fraction = 1, count = 0;\r
9780 \r
9781   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9782   for (n = 0; n < factor; n++)\r
9783     fraction *= 2;\r
9784   for (n = 0; n < factor; n++) {\r
9785     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9786     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9787     count ++;\r
9788     fraction = fraction / 2;\r
9789   }\r
9790   \r
9791   /* Midpoint */\r
9792   frames[count] = *mid;\r
9793   count ++;\r
9794   \r
9795   /* Slow out, stepping 1/2, then 1/4, ... */\r
9796   fraction = 2;\r
9797   for (n = 0; n < factor; n++) {\r
9798     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9799     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9800     count ++;\r
9801     fraction = fraction * 2;\r
9802   }\r
9803   *nFrames = count;\r
9804 }\r
9805 \r
9806 void\r
9807 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9808 {\r
9809     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9810 \r
9811     EvalGraphSet( first, last, current, pvInfoList );\r
9812 }\r
9813 \r
9814 void\r
9815 SettingsPopUp(ChessProgramState *cps)\r
9816 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9817       EngineOptionsPopup(savedHwnd, cps);\r
9818 }\r
9819 \r
9820 int flock(int fid, int code)\r
9821 {\r
9822     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
9823     OVERLAPPED ov;\r
9824     ov.hEvent = NULL;\r
9825     ov.Offset = 0;\r
9826     ov.OffsetHigh = 0;\r
9827     switch(code) {\r
9828       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
9829       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
9830       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
9831       default: return -1;\r
9832     }\r
9833     return 0;\r
9834 }\r