Implement Load Engine dialog in WinBoard
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts. \r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 #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       break;\r
4948 \r
4949     case IDM_EditServers:\r
4950       EditTagsPopUp(icsNames, &icsNames);\r
4951       break;\r
4952 \r
4953     case IDM_EditTags:\r
4954     case IDM_Tags:\r
4955       EditTagsProc();\r
4956       break;\r
4957 \r
4958     case IDM_EditComment:\r
4959     case IDM_Comment:\r
4960       if (commentUp && editComment) {\r
4961         CommentPopDown();\r
4962       } else {\r
4963         EditCommentEvent();\r
4964       }\r
4965       break;\r
4966 \r
4967     case IDM_Pause:\r
4968       PauseEvent();\r
4969       break;\r
4970 \r
4971     case IDM_Accept:\r
4972       AcceptEvent();\r
4973       break;\r
4974 \r
4975     case IDM_Decline:\r
4976       DeclineEvent();\r
4977       break;\r
4978 \r
4979     case IDM_Rematch:\r
4980       RematchEvent();\r
4981       break;\r
4982 \r
4983     case IDM_CallFlag:\r
4984       CallFlagEvent();\r
4985       break;\r
4986 \r
4987     case IDM_Draw:\r
4988       DrawEvent();\r
4989       break;\r
4990 \r
4991     case IDM_Adjourn:\r
4992       AdjournEvent();\r
4993       break;\r
4994 \r
4995     case IDM_Abort:\r
4996       AbortEvent();\r
4997       break;\r
4998 \r
4999     case IDM_Resign:\r
5000       ResignEvent();\r
5001       break;\r
5002 \r
5003     case IDM_StopObserving:\r
5004       StopObservingEvent();\r
5005       break;\r
5006 \r
5007     case IDM_StopExamining:\r
5008       StopExaminingEvent();\r
5009       break;\r
5010 \r
5011     case IDM_Upload:\r
5012       UploadGameEvent();\r
5013       break;\r
5014 \r
5015     case IDM_TypeInMove:\r
5016       TypeInEvent('\000');\r
5017       break;\r
5018 \r
5019     case IDM_TypeInName:\r
5020       PopUpNameDialog('\000');\r
5021       break;\r
5022 \r
5023     case IDM_Backward:\r
5024       BackwardEvent();\r
5025       SetFocus(hwndMain);\r
5026       break;\r
5027 \r
5028     JAWS_MENU_ITEMS\r
5029 \r
5030     case IDM_Forward:\r
5031       ForwardEvent();\r
5032       SetFocus(hwndMain);\r
5033       break;\r
5034 \r
5035     case IDM_ToStart:\r
5036       ToStartEvent();\r
5037       SetFocus(hwndMain);\r
5038       break;\r
5039 \r
5040     case IDM_ToEnd:\r
5041       ToEndEvent();\r
5042       SetFocus(hwndMain);\r
5043       break;\r
5044 \r
5045     case IDM_Revert:\r
5046       RevertEvent(FALSE);\r
5047       break;\r
5048 \r
5049     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5050       RevertEvent(TRUE);\r
5051       break;\r
5052 \r
5053     case IDM_TruncateGame:\r
5054       TruncateGameEvent();\r
5055       break;\r
5056 \r
5057     case IDM_MoveNow:\r
5058       MoveNowEvent();\r
5059       break;\r
5060 \r
5061     case IDM_RetractMove:\r
5062       RetractMoveEvent();\r
5063       break;\r
5064 \r
5065     case IDM_FlipView:\r
5066       flipView = !flipView;\r
5067       DrawPosition(FALSE, NULL);\r
5068       break;\r
5069 \r
5070     case IDM_FlipClock:\r
5071       flipClock = !flipClock;\r
5072       DisplayBothClocks();\r
5073       DisplayLogos();\r
5074       break;\r
5075 \r
5076     case IDM_MuteSounds:\r
5077       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5078       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5079                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5080       break;\r
5081 \r
5082     case IDM_GeneralOptions:\r
5083       GeneralOptionsPopup(hwnd);\r
5084       DrawPosition(TRUE, NULL);\r
5085       break;\r
5086 \r
5087     case IDM_BoardOptions:\r
5088       BoardOptionsPopup(hwnd);\r
5089       break;\r
5090 \r
5091     case IDM_EnginePlayOptions:\r
5092       EnginePlayOptionsPopup(hwnd);\r
5093       break;\r
5094 \r
5095     case IDM_Engine1Options:\r
5096       EngineOptionsPopup(hwnd, &first);\r
5097       break;\r
5098 \r
5099     case IDM_Engine2Options:\r
5100       savedHwnd = hwnd;\r
5101       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5102       EngineOptionsPopup(hwnd, &second);\r
5103       break;\r
5104 \r
5105     case IDM_OptionsUCI:\r
5106       UciOptionsPopup(hwnd);\r
5107       break;\r
5108 \r
5109     case IDM_IcsOptions:\r
5110       IcsOptionsPopup(hwnd);\r
5111       break;\r
5112 \r
5113     case IDM_Fonts:\r
5114       FontsOptionsPopup(hwnd);\r
5115       break;\r
5116 \r
5117     case IDM_Sounds:\r
5118       SoundOptionsPopup(hwnd);\r
5119       break;\r
5120 \r
5121     case IDM_CommPort:\r
5122       CommPortOptionsPopup(hwnd);\r
5123       break;\r
5124 \r
5125     case IDM_LoadOptions:\r
5126       LoadOptionsPopup(hwnd);\r
5127       break;\r
5128 \r
5129     case IDM_SaveOptions:\r
5130       SaveOptionsPopup(hwnd);\r
5131       break;\r
5132 \r
5133     case IDM_TimeControl:\r
5134       TimeControlOptionsPopup(hwnd);\r
5135       break;\r
5136 \r
5137     case IDM_SaveSettings:\r
5138       SaveSettings(settingsFileName);\r
5139       break;\r
5140 \r
5141     case IDM_SaveSettingsOnExit:\r
5142       saveSettingsOnExit = !saveSettingsOnExit;\r
5143       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5144                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5145                                          MF_CHECKED : MF_UNCHECKED));\r
5146       break;\r
5147 \r
5148     case IDM_Hint:\r
5149       HintEvent();\r
5150       break;\r
5151 \r
5152     case IDM_Book:\r
5153       BookEvent();\r
5154       break;\r
5155 \r
5156     case IDM_AboutGame:\r
5157       AboutGameEvent();\r
5158       break;\r
5159 \r
5160     case IDM_Debug:\r
5161       appData.debugMode = !appData.debugMode;\r
5162       if (appData.debugMode) {\r
5163         char dir[MSG_SIZ];\r
5164         GetCurrentDirectory(MSG_SIZ, dir);\r
5165         SetCurrentDirectory(installDir);\r
5166         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5167         SetCurrentDirectory(dir);\r
5168         setbuf(debugFP, NULL);\r
5169       } else {\r
5170         fclose(debugFP);\r
5171         debugFP = NULL;\r
5172       }\r
5173       break;\r
5174 \r
5175     case IDM_HELPCONTENTS:\r
5176       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5177           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5178           MessageBox (GetFocus(),\r
5179                     _("Unable to activate help"),\r
5180                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5181       }\r
5182       break;\r
5183 \r
5184     case IDM_HELPSEARCH:\r
5185         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5186             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5187         MessageBox (GetFocus(),\r
5188                     _("Unable to activate help"),\r
5189                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5190       }\r
5191       break;\r
5192 \r
5193     case IDM_HELPHELP:\r
5194       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5195         MessageBox (GetFocus(),\r
5196                     _("Unable to activate help"),\r
5197                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5198       }\r
5199       break;\r
5200 \r
5201     case IDM_ABOUT:\r
5202       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5203       DialogBox(hInst, \r
5204         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5205         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5206       FreeProcInstance(lpProc);\r
5207       break;\r
5208 \r
5209     case IDM_DirectCommand1:\r
5210       AskQuestionEvent(_("Direct Command"),\r
5211                        _("Send to chess program:"), "", "1");\r
5212       break;\r
5213     case IDM_DirectCommand2:\r
5214       AskQuestionEvent(_("Direct Command"),\r
5215                        _("Send to second chess program:"), "", "2");\r
5216       break;\r
5217 \r
5218     case EP_WhitePawn:\r
5219       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5220       fromX = fromY = -1;\r
5221       break;\r
5222 \r
5223     case EP_WhiteKnight:\r
5224       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5225       fromX = fromY = -1;\r
5226       break;\r
5227 \r
5228     case EP_WhiteBishop:\r
5229       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5230       fromX = fromY = -1;\r
5231       break;\r
5232 \r
5233     case EP_WhiteRook:\r
5234       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5235       fromX = fromY = -1;\r
5236       break;\r
5237 \r
5238     case EP_WhiteQueen:\r
5239       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5240       fromX = fromY = -1;\r
5241       break;\r
5242 \r
5243     case EP_WhiteFerz:\r
5244       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5245       fromX = fromY = -1;\r
5246       break;\r
5247 \r
5248     case EP_WhiteWazir:\r
5249       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5250       fromX = fromY = -1;\r
5251       break;\r
5252 \r
5253     case EP_WhiteAlfil:\r
5254       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5255       fromX = fromY = -1;\r
5256       break;\r
5257 \r
5258     case EP_WhiteCannon:\r
5259       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5260       fromX = fromY = -1;\r
5261       break;\r
5262 \r
5263     case EP_WhiteCardinal:\r
5264       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5265       fromX = fromY = -1;\r
5266       break;\r
5267 \r
5268     case EP_WhiteMarshall:\r
5269       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5270       fromX = fromY = -1;\r
5271       break;\r
5272 \r
5273     case EP_WhiteKing:\r
5274       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5275       fromX = fromY = -1;\r
5276       break;\r
5277 \r
5278     case EP_BlackPawn:\r
5279       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5280       fromX = fromY = -1;\r
5281       break;\r
5282 \r
5283     case EP_BlackKnight:\r
5284       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5285       fromX = fromY = -1;\r
5286       break;\r
5287 \r
5288     case EP_BlackBishop:\r
5289       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5290       fromX = fromY = -1;\r
5291       break;\r
5292 \r
5293     case EP_BlackRook:\r
5294       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5295       fromX = fromY = -1;\r
5296       break;\r
5297 \r
5298     case EP_BlackQueen:\r
5299       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5300       fromX = fromY = -1;\r
5301       break;\r
5302 \r
5303     case EP_BlackFerz:\r
5304       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5305       fromX = fromY = -1;\r
5306       break;\r
5307 \r
5308     case EP_BlackWazir:\r
5309       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5310       fromX = fromY = -1;\r
5311       break;\r
5312 \r
5313     case EP_BlackAlfil:\r
5314       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5315       fromX = fromY = -1;\r
5316       break;\r
5317 \r
5318     case EP_BlackCannon:\r
5319       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5320       fromX = fromY = -1;\r
5321       break;\r
5322 \r
5323     case EP_BlackCardinal:\r
5324       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5325       fromX = fromY = -1;\r
5326       break;\r
5327 \r
5328     case EP_BlackMarshall:\r
5329       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5330       fromX = fromY = -1;\r
5331       break;\r
5332 \r
5333     case EP_BlackKing:\r
5334       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5335       fromX = fromY = -1;\r
5336       break;\r
5337 \r
5338     case EP_EmptySquare:\r
5339       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5340       fromX = fromY = -1;\r
5341       break;\r
5342 \r
5343     case EP_ClearBoard:\r
5344       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5345       fromX = fromY = -1;\r
5346       break;\r
5347 \r
5348     case EP_White:\r
5349       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5350       fromX = fromY = -1;\r
5351       break;\r
5352 \r
5353     case EP_Black:\r
5354       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5355       fromX = fromY = -1;\r
5356       break;\r
5357 \r
5358     case EP_Promote:\r
5359       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5360       fromX = fromY = -1;\r
5361       break;\r
5362 \r
5363     case EP_Demote:\r
5364       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5365       fromX = fromY = -1;\r
5366       break;\r
5367 \r
5368     case DP_Pawn:\r
5369       DropMenuEvent(WhitePawn, fromX, fromY);\r
5370       fromX = fromY = -1;\r
5371       break;\r
5372 \r
5373     case DP_Knight:\r
5374       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5375       fromX = fromY = -1;\r
5376       break;\r
5377 \r
5378     case DP_Bishop:\r
5379       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5380       fromX = fromY = -1;\r
5381       break;\r
5382 \r
5383     case DP_Rook:\r
5384       DropMenuEvent(WhiteRook, fromX, fromY);\r
5385       fromX = fromY = -1;\r
5386       break;\r
5387 \r
5388     case DP_Queen:\r
5389       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5390       fromX = fromY = -1;\r
5391       break;\r
5392 \r
5393     case IDM_English:\r
5394       barbaric = 0; appData.language = "";\r
5395       TranslateMenus(0);\r
5396       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5397       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5398       lastChecked = wmId;\r
5399       break;\r
5400 \r
5401     default:\r
5402       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5403           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5404           TranslateMenus(0);\r
5405           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5406           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5407           lastChecked = wmId;\r
5408           break;\r
5409       }\r
5410       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5411     }\r
5412     break;\r
5413 \r
5414   case WM_TIMER:\r
5415     switch (wParam) {\r
5416     case CLOCK_TIMER_ID:\r
5417       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5418       clockTimerEvent = 0;\r
5419       DecrementClocks(); /* call into back end */\r
5420       break;\r
5421     case LOAD_GAME_TIMER_ID:\r
5422       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5423       loadGameTimerEvent = 0;\r
5424       AutoPlayGameLoop(); /* call into back end */\r
5425       break;\r
5426     case ANALYSIS_TIMER_ID:\r
5427       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5428                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5429         AnalysisPeriodicEvent(0);\r
5430       } else {\r
5431         KillTimer(hwnd, analysisTimerEvent);\r
5432         analysisTimerEvent = 0;\r
5433       }\r
5434       break;\r
5435     case DELAYED_TIMER_ID:\r
5436       KillTimer(hwnd, delayedTimerEvent);\r
5437       delayedTimerEvent = 0;\r
5438       delayedTimerCallback();\r
5439       break;\r
5440     }\r
5441     break;\r
5442 \r
5443   case WM_USER_Input:\r
5444     InputEvent(hwnd, message, wParam, lParam);\r
5445     break;\r
5446 \r
5447   /* [AS] Also move "attached" child windows */\r
5448   case WM_WINDOWPOSCHANGING:\r
5449 \r
5450     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5451         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5452 \r
5453         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5454             /* Window is moving */\r
5455             RECT rcMain;\r
5456 \r
5457 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5458             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5459             rcMain.right  = wpMain.x + wpMain.width;\r
5460             rcMain.top    = wpMain.y;\r
5461             rcMain.bottom = wpMain.y + wpMain.height;\r
5462             \r
5463             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5464             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5465             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5466             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5467             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5468             wpMain.x = lpwp->x;\r
5469             wpMain.y = lpwp->y;\r
5470         }\r
5471     }\r
5472     break;\r
5473 \r
5474   /* [AS] Snapping */\r
5475   case WM_ENTERSIZEMOVE:\r
5476     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5477     if (hwnd == hwndMain) {\r
5478       doingSizing = TRUE;\r
5479       lastSizing = 0;\r
5480     }\r
5481     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5482     break;\r
5483 \r
5484   case WM_SIZING:\r
5485     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5486     if (hwnd == hwndMain) {\r
5487       lastSizing = wParam;\r
5488     }\r
5489     break;\r
5490 \r
5491   case WM_MOVING:\r
5492     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5493       return OnMoving( &sd, hwnd, wParam, lParam );\r
5494 \r
5495   case WM_EXITSIZEMOVE:\r
5496     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5497     if (hwnd == hwndMain) {\r
5498       RECT client;\r
5499       doingSizing = FALSE;\r
5500       InvalidateRect(hwnd, &boardRect, FALSE);\r
5501       GetClientRect(hwnd, &client);\r
5502       ResizeBoard(client.right, client.bottom, lastSizing);\r
5503       lastSizing = 0;\r
5504       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5505     }\r
5506     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5507     break;\r
5508 \r
5509   case WM_DESTROY: /* message: window being destroyed */\r
5510     PostQuitMessage(0);\r
5511     break;\r
5512 \r
5513   case WM_CLOSE:\r
5514     if (hwnd == hwndMain) {\r
5515       ExitEvent(0);\r
5516     }\r
5517     break;\r
5518 \r
5519   default:      /* Passes it on if unprocessed */\r
5520     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5521   }\r
5522   return 0;\r
5523 }\r
5524 \r
5525 /*---------------------------------------------------------------------------*\\r
5526  *\r
5527  * Misc utility routines\r
5528  *\r
5529 \*---------------------------------------------------------------------------*/\r
5530 \r
5531 /*\r
5532  * Decent random number generator, at least not as bad as Windows\r
5533  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5534  */\r
5535 unsigned int randstate;\r
5536 \r
5537 int\r
5538 myrandom(void)\r
5539 {\r
5540   randstate = randstate * 1664525 + 1013904223;\r
5541   return (int) randstate & 0x7fffffff;\r
5542 }\r
5543 \r
5544 void\r
5545 mysrandom(unsigned int seed)\r
5546 {\r
5547   randstate = seed;\r
5548 }\r
5549 \r
5550 \r
5551 /* \r
5552  * returns TRUE if user selects a different color, FALSE otherwise \r
5553  */\r
5554 \r
5555 BOOL\r
5556 ChangeColor(HWND hwnd, COLORREF *which)\r
5557 {\r
5558   static BOOL firstTime = TRUE;\r
5559   static DWORD customColors[16];\r
5560   CHOOSECOLOR cc;\r
5561   COLORREF newcolor;\r
5562   int i;\r
5563   ColorClass ccl;\r
5564 \r
5565   if (firstTime) {\r
5566     /* Make initial colors in use available as custom colors */\r
5567     /* Should we put the compiled-in defaults here instead? */\r
5568     i = 0;\r
5569     customColors[i++] = lightSquareColor & 0xffffff;\r
5570     customColors[i++] = darkSquareColor & 0xffffff;\r
5571     customColors[i++] = whitePieceColor & 0xffffff;\r
5572     customColors[i++] = blackPieceColor & 0xffffff;\r
5573     customColors[i++] = highlightSquareColor & 0xffffff;\r
5574     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5575 \r
5576     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5577       customColors[i++] = textAttribs[ccl].color;\r
5578     }\r
5579     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5580     firstTime = FALSE;\r
5581   }\r
5582 \r
5583   cc.lStructSize = sizeof(cc);\r
5584   cc.hwndOwner = hwnd;\r
5585   cc.hInstance = NULL;\r
5586   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5587   cc.lpCustColors = (LPDWORD) customColors;\r
5588   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5589 \r
5590   if (!ChooseColor(&cc)) return FALSE;\r
5591 \r
5592   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5593   if (newcolor == *which) return FALSE;\r
5594   *which = newcolor;\r
5595   return TRUE;\r
5596 \r
5597   /*\r
5598   InitDrawingColors();\r
5599   InvalidateRect(hwnd, &boardRect, FALSE);\r
5600   */\r
5601 }\r
5602 \r
5603 BOOLEAN\r
5604 MyLoadSound(MySound *ms)\r
5605 {\r
5606   BOOL ok = FALSE;\r
5607   struct stat st;\r
5608   FILE *f;\r
5609 \r
5610   if (ms->data) free(ms->data);\r
5611   ms->data = NULL;\r
5612 \r
5613   switch (ms->name[0]) {\r
5614   case NULLCHAR:\r
5615     /* Silence */\r
5616     ok = TRUE;\r
5617     break;\r
5618   case '$':\r
5619     /* System sound from Control Panel.  Don't preload here. */\r
5620     ok = TRUE;\r
5621     break;\r
5622   case '!':\r
5623     if (ms->name[1] == NULLCHAR) {\r
5624       /* "!" alone = silence */\r
5625       ok = TRUE;\r
5626     } else {\r
5627       /* Builtin wave resource.  Error if not found. */\r
5628       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5629       if (h == NULL) break;\r
5630       ms->data = (void *)LoadResource(hInst, h);\r
5631       if (h == NULL) break;\r
5632       ok = TRUE;\r
5633     }\r
5634     break;\r
5635   default:\r
5636     /* .wav file.  Error if not found. */\r
5637     f = fopen(ms->name, "rb");\r
5638     if (f == NULL) break;\r
5639     if (fstat(fileno(f), &st) < 0) break;\r
5640     ms->data = malloc(st.st_size);\r
5641     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5642     fclose(f);\r
5643     ok = TRUE;\r
5644     break;\r
5645   }\r
5646   if (!ok) {\r
5647     char buf[MSG_SIZ];\r
5648       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5649     DisplayError(buf, GetLastError());\r
5650   }\r
5651   return ok;\r
5652 }\r
5653 \r
5654 BOOLEAN\r
5655 MyPlaySound(MySound *ms)\r
5656 {\r
5657   BOOLEAN ok = FALSE;\r
5658 \r
5659   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5660   switch (ms->name[0]) {\r
5661   case NULLCHAR:\r
5662         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5663     /* Silence */\r
5664     ok = TRUE;\r
5665     break;\r
5666   case '$':\r
5667     /* System sound from Control Panel (deprecated feature).\r
5668        "$" alone or an unset sound name gets default beep (still in use). */\r
5669     if (ms->name[1]) {\r
5670       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5671     }\r
5672     if (!ok) ok = MessageBeep(MB_OK);\r
5673     break; \r
5674   case '!':\r
5675     /* Builtin wave resource, or "!" alone for silence */\r
5676     if (ms->name[1]) {\r
5677       if (ms->data == NULL) return FALSE;\r
5678       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5679     } else {\r
5680       ok = TRUE;\r
5681     }\r
5682     break;\r
5683   default:\r
5684     /* .wav file.  Error if not found. */\r
5685     if (ms->data == NULL) return FALSE;\r
5686     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5687     break;\r
5688   }\r
5689   /* Don't print an error: this can happen innocently if the sound driver\r
5690      is busy; for instance, if another instance of WinBoard is playing\r
5691      a sound at about the same time. */\r
5692   return ok;\r
5693 }\r
5694 \r
5695 \r
5696 LRESULT CALLBACK\r
5697 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5698 {\r
5699   BOOL ok;\r
5700   OPENFILENAME *ofn;\r
5701   static UINT *number; /* gross that this is static */\r
5702 \r
5703   switch (message) {\r
5704   case WM_INITDIALOG: /* message: initialize dialog box */\r
5705     /* Center the dialog over the application window */\r
5706     ofn = (OPENFILENAME *) lParam;\r
5707     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5708       number = (UINT *) ofn->lCustData;\r
5709       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5710     } else {\r
5711       number = NULL;\r
5712     }\r
5713     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5714     Translate(hDlg, 1536);\r
5715     return FALSE;  /* Allow for further processing */\r
5716 \r
5717   case WM_COMMAND:\r
5718     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5719       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5720     }\r
5721     return FALSE;  /* Allow for further processing */\r
5722   }\r
5723   return FALSE;\r
5724 }\r
5725 \r
5726 UINT APIENTRY\r
5727 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5728 {\r
5729   static UINT *number;\r
5730   OPENFILENAME *ofname;\r
5731   OFNOTIFY *ofnot;\r
5732   switch (uiMsg) {\r
5733   case WM_INITDIALOG:\r
5734     Translate(hdlg, DLG_IndexNumber);\r
5735     ofname = (OPENFILENAME *)lParam;\r
5736     number = (UINT *)(ofname->lCustData);\r
5737     break;\r
5738   case WM_NOTIFY:\r
5739     ofnot = (OFNOTIFY *)lParam;\r
5740     if (ofnot->hdr.code == CDN_FILEOK) {\r
5741       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5742     }\r
5743     break;\r
5744   }\r
5745   return 0;\r
5746 }\r
5747 \r
5748 \r
5749 FILE *\r
5750 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5751                char *nameFilt, char *dlgTitle, UINT *number,\r
5752                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5753 {\r
5754   OPENFILENAME openFileName;\r
5755   char buf1[MSG_SIZ];\r
5756   FILE *f;\r
5757 \r
5758   if (fileName == NULL) fileName = buf1;\r
5759   if (defName == NULL) {\r
5760     safeStrCpy(fileName, "*.", 3 );\r
5761     strcat(fileName, defExt);\r
5762   } else {\r
5763     safeStrCpy(fileName, defName, MSG_SIZ );\r
5764   }\r
5765     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5766   if (number) *number = 0;\r
5767 \r
5768   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5769   openFileName.hwndOwner         = hwnd;\r
5770   openFileName.hInstance         = (HANDLE) hInst;\r
5771   openFileName.lpstrFilter       = nameFilt;\r
5772   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5773   openFileName.nMaxCustFilter    = 0L;\r
5774   openFileName.nFilterIndex      = 1L;\r
5775   openFileName.lpstrFile         = fileName;\r
5776   openFileName.nMaxFile          = MSG_SIZ;\r
5777   openFileName.lpstrFileTitle    = fileTitle;\r
5778   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5779   openFileName.lpstrInitialDir   = NULL;\r
5780   openFileName.lpstrTitle        = dlgTitle;\r
5781   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5782     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5783     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5784     | (oldDialog ? 0 : OFN_EXPLORER);\r
5785   openFileName.nFileOffset       = 0;\r
5786   openFileName.nFileExtension    = 0;\r
5787   openFileName.lpstrDefExt       = defExt;\r
5788   openFileName.lCustData         = (LONG) number;\r
5789   openFileName.lpfnHook          = oldDialog ?\r
5790     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5791   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5792 \r
5793   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5794                         GetOpenFileName(&openFileName)) {\r
5795     /* open the file */\r
5796     f = fopen(openFileName.lpstrFile, write);\r
5797     if (f == NULL) {\r
5798       MessageBox(hwnd, _("File open failed"), NULL,\r
5799                  MB_OK|MB_ICONEXCLAMATION);\r
5800       return NULL;\r
5801     }\r
5802   } else {\r
5803     int err = CommDlgExtendedError();\r
5804     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5805     return FALSE;\r
5806   }\r
5807   return f;\r
5808 }\r
5809 \r
5810 \r
5811 \r
5812 VOID APIENTRY\r
5813 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5814 {\r
5815   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5816 \r
5817   /*\r
5818    * Get the first pop-up menu in the menu template. This is the\r
5819    * menu that TrackPopupMenu displays.\r
5820    */\r
5821   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5822   TranslateOneMenu(10, hmenuTrackPopup);\r
5823 \r
5824   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5825 \r
5826   /*\r
5827    * TrackPopup uses screen coordinates, so convert the\r
5828    * coordinates of the mouse click to screen coordinates.\r
5829    */\r
5830   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5831 \r
5832   /* Draw and track the floating pop-up menu. */\r
5833   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5834                  pt.x, pt.y, 0, hwnd, NULL);\r
5835 \r
5836   /* Destroy the menu.*/\r
5837   DestroyMenu(hmenu);\r
5838 }\r
5839    \r
5840 typedef struct {\r
5841   HWND hDlg, hText;\r
5842   int sizeX, sizeY, newSizeX, newSizeY;\r
5843   HDWP hdwp;\r
5844 } ResizeEditPlusButtonsClosure;\r
5845 \r
5846 BOOL CALLBACK\r
5847 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5848 {\r
5849   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5850   RECT rect;\r
5851   POINT pt;\r
5852 \r
5853   if (hChild == cl->hText) return TRUE;\r
5854   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5855   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5856   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5857   ScreenToClient(cl->hDlg, &pt);\r
5858   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5859     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5860   return TRUE;\r
5861 }\r
5862 \r
5863 /* Resize a dialog that has a (rich) edit field filling most of\r
5864    the top, with a row of buttons below */\r
5865 VOID\r
5866 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5867 {\r
5868   RECT rectText;\r
5869   int newTextHeight, newTextWidth;\r
5870   ResizeEditPlusButtonsClosure cl;\r
5871   \r
5872   /*if (IsIconic(hDlg)) return;*/\r
5873   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5874   \r
5875   cl.hdwp = BeginDeferWindowPos(8);\r
5876 \r
5877   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5878   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5879   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5880   if (newTextHeight < 0) {\r
5881     newSizeY += -newTextHeight;\r
5882     newTextHeight = 0;\r
5883   }\r
5884   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5885     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5886 \r
5887   cl.hDlg = hDlg;\r
5888   cl.hText = hText;\r
5889   cl.sizeX = sizeX;\r
5890   cl.sizeY = sizeY;\r
5891   cl.newSizeX = newSizeX;\r
5892   cl.newSizeY = newSizeY;\r
5893   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5894 \r
5895   EndDeferWindowPos(cl.hdwp);\r
5896 }\r
5897 \r
5898 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5899 {\r
5900     RECT    rChild, rParent;\r
5901     int     wChild, hChild, wParent, hParent;\r
5902     int     wScreen, hScreen, xNew, yNew;\r
5903     HDC     hdc;\r
5904 \r
5905     /* Get the Height and Width of the child window */\r
5906     GetWindowRect (hwndChild, &rChild);\r
5907     wChild = rChild.right - rChild.left;\r
5908     hChild = rChild.bottom - rChild.top;\r
5909 \r
5910     /* Get the Height and Width of the parent window */\r
5911     GetWindowRect (hwndParent, &rParent);\r
5912     wParent = rParent.right - rParent.left;\r
5913     hParent = rParent.bottom - rParent.top;\r
5914 \r
5915     /* Get the display limits */\r
5916     hdc = GetDC (hwndChild);\r
5917     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5918     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5919     ReleaseDC(hwndChild, hdc);\r
5920 \r
5921     /* Calculate new X position, then adjust for screen */\r
5922     xNew = rParent.left + ((wParent - wChild) /2);\r
5923     if (xNew < 0) {\r
5924         xNew = 0;\r
5925     } else if ((xNew+wChild) > wScreen) {\r
5926         xNew = wScreen - wChild;\r
5927     }\r
5928 \r
5929     /* Calculate new Y position, then adjust for screen */\r
5930     if( mode == 0 ) {\r
5931         yNew = rParent.top  + ((hParent - hChild) /2);\r
5932     }\r
5933     else {\r
5934         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5935     }\r
5936 \r
5937     if (yNew < 0) {\r
5938         yNew = 0;\r
5939     } else if ((yNew+hChild) > hScreen) {\r
5940         yNew = hScreen - hChild;\r
5941     }\r
5942 \r
5943     /* Set it, and return */\r
5944     return SetWindowPos (hwndChild, NULL,\r
5945                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5946 }\r
5947 \r
5948 /* Center one window over another */\r
5949 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
5950 {\r
5951     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
5952 }\r
5953 \r
5954 /*---------------------------------------------------------------------------*\\r
5955  *\r
5956  * Startup Dialog functions\r
5957  *\r
5958 \*---------------------------------------------------------------------------*/\r
5959 void\r
5960 InitComboStrings(HANDLE hwndCombo, char **cd)\r
5961 {\r
5962   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5963 \r
5964   while (*cd != NULL) {\r
5965     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
5966     cd++;\r
5967   }\r
5968 }\r
5969 \r
5970 void\r
5971 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
5972 {\r
5973   char buf1[MAX_ARG_LEN];\r
5974   int len;\r
5975 \r
5976   if (str[0] == '@') {\r
5977     FILE* f = fopen(str + 1, "r");\r
5978     if (f == NULL) {\r
5979       DisplayFatalError(str + 1, errno, 2);\r
5980       return;\r
5981     }\r
5982     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
5983     fclose(f);\r
5984     buf1[len] = NULLCHAR;\r
5985     str = buf1;\r
5986   }\r
5987 \r
5988   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5989 \r
5990   for (;;) {\r
5991     char buf[MSG_SIZ];\r
5992     char *end = strchr(str, '\n');\r
5993     if (end == NULL) return;\r
5994     memcpy(buf, str, end - str);\r
5995     buf[end - str] = NULLCHAR;\r
5996     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
5997     str = end + 1;\r
5998   }\r
5999 }\r
6000 \r
6001 void\r
6002 SetStartupDialogEnables(HWND hDlg)\r
6003 {\r
6004   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6005     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6006     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6007   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6008     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6009   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6010     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6011   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6012     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6013   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6014     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6015     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6016     IsDlgButtonChecked(hDlg, OPT_View));\r
6017 }\r
6018 \r
6019 char *\r
6020 QuoteForFilename(char *filename)\r
6021 {\r
6022   int dquote, space;\r
6023   dquote = strchr(filename, '"') != NULL;\r
6024   space = strchr(filename, ' ') != NULL;\r
6025   if (dquote || space) {\r
6026     if (dquote) {\r
6027       return "'";\r
6028     } else {\r
6029       return "\"";\r
6030     }\r
6031   } else {\r
6032     return "";\r
6033   }\r
6034 }\r
6035 \r
6036 VOID\r
6037 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6038 {\r
6039   char buf[MSG_SIZ];\r
6040   char *q;\r
6041 \r
6042   InitComboStringsFromOption(hwndCombo, nthnames);\r
6043   q = QuoteForFilename(nthcp);\r
6044     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6045   if (*nthdir != NULLCHAR) {\r
6046     q = QuoteForFilename(nthdir);\r
6047       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6048   }\r
6049   if (*nthcp == NULLCHAR) {\r
6050     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6051   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6052     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6053     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6054   }\r
6055 }\r
6056 \r
6057 LRESULT CALLBACK\r
6058 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6059 {\r
6060   char buf[MSG_SIZ];\r
6061   HANDLE hwndCombo;\r
6062   char *p;\r
6063 \r
6064   switch (message) {\r
6065   case WM_INITDIALOG:\r
6066     /* Center the dialog */\r
6067     CenterWindow (hDlg, GetDesktopWindow());\r
6068     Translate(hDlg, DLG_Startup);\r
6069     /* Initialize the dialog items */\r
6070     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6071                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6072                   firstChessProgramNames);\r
6073     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6074                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6075                   secondChessProgramNames);\r
6076     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6077     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6078       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6079     if (*appData.icsHelper != NULLCHAR) {\r
6080       char *q = QuoteForFilename(appData.icsHelper);\r
6081       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6082     }\r
6083     if (*appData.icsHost == NULLCHAR) {\r
6084       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6085       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6086     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6087       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6088       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6089     }\r
6090 \r
6091     if (appData.icsActive) {\r
6092       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6093     }\r
6094     else if (appData.noChessProgram) {\r
6095       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6096     }\r
6097     else {\r
6098       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6099     }\r
6100 \r
6101     SetStartupDialogEnables(hDlg);\r
6102     return TRUE;\r
6103 \r
6104   case WM_COMMAND:\r
6105     switch (LOWORD(wParam)) {\r
6106     case IDOK:\r
6107       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6108         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6109         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6110         p = buf;\r
6111         ParseArgs(StringGet, &p);\r
6112         safeStrCpy(buf, "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6113         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6114         p = buf;\r
6115         ParseArgs(StringGet, &p);\r
6116         appData.noChessProgram = FALSE;\r
6117         appData.icsActive = FALSE;\r
6118       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6119         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6120         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6121         p = buf;\r
6122         ParseArgs(StringGet, &p);\r
6123         if (appData.zippyPlay) {\r
6124           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6125           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6126           p = buf;\r
6127           ParseArgs(StringGet, &p);\r
6128         }\r
6129       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6130         appData.noChessProgram = TRUE;\r
6131         appData.icsActive = FALSE;\r
6132       } else {\r
6133         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6134                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6135         return TRUE;\r
6136       }\r
6137       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6138         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6139         p = buf;\r
6140         ParseArgs(StringGet, &p);\r
6141       }\r
6142       EndDialog(hDlg, TRUE);\r
6143       return TRUE;\r
6144 \r
6145     case IDCANCEL:\r
6146       ExitEvent(0);\r
6147       return TRUE;\r
6148 \r
6149     case IDM_HELPCONTENTS:\r
6150       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6151         MessageBox (GetFocus(),\r
6152                     _("Unable to activate help"),\r
6153                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6154       }\r
6155       break;\r
6156 \r
6157     default:\r
6158       SetStartupDialogEnables(hDlg);\r
6159       break;\r
6160     }\r
6161     break;\r
6162   }\r
6163   return FALSE;\r
6164 }\r
6165 \r
6166 /*---------------------------------------------------------------------------*\\r
6167  *\r
6168  * About box dialog functions\r
6169  *\r
6170 \*---------------------------------------------------------------------------*/\r
6171 \r
6172 /* Process messages for "About" dialog box */\r
6173 LRESULT CALLBACK\r
6174 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6175 {\r
6176   switch (message) {\r
6177   case WM_INITDIALOG: /* message: initialize dialog box */\r
6178     /* Center the dialog over the application window */\r
6179     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6180     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6181     Translate(hDlg, ABOUTBOX);\r
6182     JAWS_COPYRIGHT\r
6183     return (TRUE);\r
6184 \r
6185   case WM_COMMAND: /* message: received a command */\r
6186     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6187         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6188       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6189       return (TRUE);\r
6190     }\r
6191     break;\r
6192   }\r
6193   return (FALSE);\r
6194 }\r
6195 \r
6196 /*---------------------------------------------------------------------------*\\r
6197  *\r
6198  * Comment Dialog functions\r
6199  *\r
6200 \*---------------------------------------------------------------------------*/\r
6201 \r
6202 LRESULT CALLBACK\r
6203 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6204 {\r
6205   static HANDLE hwndText = NULL;\r
6206   int len, newSizeX, newSizeY, flags;\r
6207   static int sizeX, sizeY;\r
6208   char *str;\r
6209   RECT rect;\r
6210   MINMAXINFO *mmi;\r
6211 \r
6212   switch (message) {\r
6213   case WM_INITDIALOG: /* message: initialize dialog box */\r
6214     /* Initialize the dialog items */\r
6215     Translate(hDlg, DLG_EditComment);\r
6216     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6217     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6218     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6219     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6220     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6221     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6222     SetWindowText(hDlg, commentTitle);\r
6223     if (editComment) {\r
6224       SetFocus(hwndText);\r
6225     } else {\r
6226       SetFocus(GetDlgItem(hDlg, IDOK));\r
6227     }\r
6228     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6229                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6230                 MAKELPARAM(FALSE, 0));\r
6231     /* Size and position the dialog */\r
6232     if (!commentDialog) {\r
6233       commentDialog = hDlg;\r
6234       flags = SWP_NOZORDER;\r
6235       GetClientRect(hDlg, &rect);\r
6236       sizeX = rect.right;\r
6237       sizeY = rect.bottom;\r
6238       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6239           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6240         WINDOWPLACEMENT wp;\r
6241         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6242         wp.length = sizeof(WINDOWPLACEMENT);\r
6243         wp.flags = 0;\r
6244         wp.showCmd = SW_SHOW;\r
6245         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6246         wp.rcNormalPosition.left = wpComment.x;\r
6247         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6248         wp.rcNormalPosition.top = wpComment.y;\r
6249         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6250         SetWindowPlacement(hDlg, &wp);\r
6251 \r
6252         GetClientRect(hDlg, &rect);\r
6253         newSizeX = rect.right;\r
6254         newSizeY = rect.bottom;\r
6255         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6256                               newSizeX, newSizeY);\r
6257         sizeX = newSizeX;\r
6258         sizeY = newSizeY;\r
6259       }\r
6260     }\r
6261     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6262     return FALSE;\r
6263 \r
6264   case WM_COMMAND: /* message: received a command */\r
6265     switch (LOWORD(wParam)) {\r
6266     case IDOK:\r
6267       if (editComment) {\r
6268         char *p, *q;\r
6269         /* Read changed options from the dialog box */\r
6270         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6271         len = GetWindowTextLength(hwndText);\r
6272         str = (char *) malloc(len + 1);\r
6273         GetWindowText(hwndText, str, len + 1);\r
6274         p = q = str;\r
6275         while (*q) {\r
6276           if (*q == '\r')\r
6277             q++;\r
6278           else\r
6279             *p++ = *q++;\r
6280         }\r
6281         *p = NULLCHAR;\r
6282         ReplaceComment(commentIndex, str);\r
6283         free(str);\r
6284       }\r
6285       CommentPopDown();\r
6286       return TRUE;\r
6287 \r
6288     case IDCANCEL:\r
6289     case OPT_CancelComment:\r
6290       CommentPopDown();\r
6291       return TRUE;\r
6292 \r
6293     case OPT_ClearComment:\r
6294       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6295       break;\r
6296 \r
6297     case OPT_EditComment:\r
6298       EditCommentEvent();\r
6299       return TRUE;\r
6300 \r
6301     default:\r
6302       break;\r
6303     }\r
6304     break;\r
6305 \r
6306   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6307         if( wParam == OPT_CommentText ) {\r
6308             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6309 \r
6310             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6311                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6312                 POINTL pt;\r
6313                 LRESULT index;\r
6314 \r
6315                 pt.x = LOWORD( lpMF->lParam );\r
6316                 pt.y = HIWORD( lpMF->lParam );\r
6317 \r
6318                 if(lpMF->msg == WM_CHAR) {\r
6319                         CHARRANGE sel;\r
6320                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6321                         index = sel.cpMin;\r
6322                 } else\r
6323                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6324 \r
6325                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6326                 len = GetWindowTextLength(hwndText);\r
6327                 str = (char *) malloc(len + 1);\r
6328                 GetWindowText(hwndText, str, len + 1);\r
6329                 ReplaceComment(commentIndex, str);\r
6330                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6331                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6332                 free(str);\r
6333 \r
6334                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6335                 lpMF->msg = WM_USER;\r
6336 \r
6337                 return TRUE;\r
6338             }\r
6339         }\r
6340         break;\r
6341 \r
6342   case WM_SIZE:\r
6343     newSizeX = LOWORD(lParam);\r
6344     newSizeY = HIWORD(lParam);\r
6345     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6346     sizeX = newSizeX;\r
6347     sizeY = newSizeY;\r
6348     break;\r
6349 \r
6350   case WM_GETMINMAXINFO:\r
6351     /* Prevent resizing window too small */\r
6352     mmi = (MINMAXINFO *) lParam;\r
6353     mmi->ptMinTrackSize.x = 100;\r
6354     mmi->ptMinTrackSize.y = 100;\r
6355     break;\r
6356   }\r
6357   return FALSE;\r
6358 }\r
6359 \r
6360 VOID\r
6361 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6362 {\r
6363   FARPROC lpProc;\r
6364   char *p, *q;\r
6365 \r
6366   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6367 \r
6368   if (str == NULL) str = "";\r
6369   p = (char *) malloc(2 * strlen(str) + 2);\r
6370   q = p;\r
6371   while (*str) {\r
6372     if (*str == '\n') *q++ = '\r';\r
6373     *q++ = *str++;\r
6374   }\r
6375   *q = NULLCHAR;\r
6376   if (commentText != NULL) free(commentText);\r
6377 \r
6378   commentIndex = index;\r
6379   commentTitle = title;\r
6380   commentText = p;\r
6381   editComment = edit;\r
6382 \r
6383   if (commentDialog) {\r
6384     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6385     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6386   } else {\r
6387     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6388     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6389                  hwndMain, (DLGPROC)lpProc);\r
6390     FreeProcInstance(lpProc);\r
6391   }\r
6392   commentUp = TRUE;\r
6393 }\r
6394 \r
6395 \r
6396 /*---------------------------------------------------------------------------*\\r
6397  *\r
6398  * Type-in move dialog functions\r
6399  * \r
6400 \*---------------------------------------------------------------------------*/\r
6401 \r
6402 LRESULT CALLBACK\r
6403 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6404 {\r
6405   char move[MSG_SIZ];\r
6406   HWND hInput;\r
6407 \r
6408   switch (message) {\r
6409   case WM_INITDIALOG:\r
6410     move[0] = (char) lParam;\r
6411     move[1] = NULLCHAR;\r
6412     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6413     Translate(hDlg, DLG_TypeInMove);\r
6414     hInput = GetDlgItem(hDlg, OPT_Move);\r
6415     SetWindowText(hInput, move);\r
6416     SetFocus(hInput);\r
6417     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6418     return FALSE;\r
6419 \r
6420   case WM_COMMAND:\r
6421     switch (LOWORD(wParam)) {\r
6422     case IDOK:
6423 \r
6424       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6425       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
6426       TypeInDoneEvent(move);\r
6427       EndDialog(hDlg, TRUE);\r
6428       return TRUE;\r
6429     case IDCANCEL:\r
6430       EndDialog(hDlg, FALSE);\r
6431       return TRUE;\r
6432     default:\r
6433       break;\r
6434     }\r
6435     break;\r
6436   }\r
6437   return FALSE;\r
6438 }\r
6439 \r
6440 VOID\r
6441 PopUpMoveDialog(char firstchar)\r
6442 {\r
6443     FARPROC lpProc;\r
6444 \r
6445       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6446       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6447         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6448       FreeProcInstance(lpProc);\r
6449 }\r
6450 \r
6451 /*---------------------------------------------------------------------------*\\r
6452  *\r
6453  * Type-in name dialog functions\r
6454  * \r
6455 \*---------------------------------------------------------------------------*/\r
6456 \r
6457 LRESULT CALLBACK\r
6458 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6459 {\r
6460   char move[MSG_SIZ];\r
6461   HWND hInput;\r
6462 \r
6463   switch (message) {\r
6464   case WM_INITDIALOG:\r
6465     move[0] = (char) lParam;\r
6466     move[1] = NULLCHAR;\r
6467     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6468     Translate(hDlg, DLG_TypeInName);\r
6469     hInput = GetDlgItem(hDlg, OPT_Name);\r
6470     SetWindowText(hInput, move);\r
6471     SetFocus(hInput);\r
6472     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6473     return FALSE;\r
6474 \r
6475   case WM_COMMAND:\r
6476     switch (LOWORD(wParam)) {\r
6477     case IDOK:\r
6478       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6479       appData.userName = strdup(move);\r
6480       SetUserLogo();\r
6481       SetGameInfo();\r
6482       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6483         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6484         DisplayTitle(move);\r
6485       }\r
6486 \r
6487 \r
6488       EndDialog(hDlg, TRUE);\r
6489       return TRUE;\r
6490     case IDCANCEL:\r
6491       EndDialog(hDlg, FALSE);\r
6492       return TRUE;\r
6493     default:\r
6494       break;\r
6495     }\r
6496     break;\r
6497   }\r
6498   return FALSE;\r
6499 }\r
6500 \r
6501 VOID\r
6502 PopUpNameDialog(char firstchar)\r
6503 {\r
6504     FARPROC lpProc;\r
6505     \r
6506       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6507       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6508         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6509       FreeProcInstance(lpProc);\r
6510 }\r
6511 \r
6512 /*---------------------------------------------------------------------------*\\r
6513  *\r
6514  *  Error dialogs\r
6515  * \r
6516 \*---------------------------------------------------------------------------*/\r
6517 \r
6518 /* Nonmodal error box */\r
6519 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6520                              WPARAM wParam, LPARAM lParam);\r
6521 \r
6522 VOID\r
6523 ErrorPopUp(char *title, char *content)\r
6524 {\r
6525   FARPROC lpProc;\r
6526   char *p, *q;\r
6527   BOOLEAN modal = hwndMain == NULL;\r
6528 \r
6529   p = content;\r
6530   q = errorMessage;\r
6531   while (*p) {\r
6532     if (*p == '\n') {\r
6533       if (modal) {\r
6534         *q++ = ' ';\r
6535         p++;\r
6536       } else {\r
6537         *q++ = '\r';\r
6538         *q++ = *p++;\r
6539       }\r
6540     } else {\r
6541       *q++ = *p++;\r
6542     }\r
6543   }\r
6544   *q = NULLCHAR;\r
6545   strncpy(errorTitle, title, sizeof(errorTitle));\r
6546   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6547   \r
6548   if (modal) {\r
6549     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6550   } else {\r
6551     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6552     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6553                  hwndMain, (DLGPROC)lpProc);\r
6554     FreeProcInstance(lpProc);\r
6555   }\r
6556 }\r
6557 \r
6558 VOID\r
6559 ErrorPopDown()\r
6560 {\r
6561   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6562   if (errorDialog == NULL) return;\r
6563   DestroyWindow(errorDialog);\r
6564   errorDialog = NULL;\r
6565   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6566 }\r
6567 \r
6568 LRESULT CALLBACK\r
6569 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6570 {\r
6571   HANDLE hwndText;\r
6572   RECT rChild;\r
6573 \r
6574   switch (message) {\r
6575   case WM_INITDIALOG:\r
6576     GetWindowRect(hDlg, &rChild);\r
6577 \r
6578     /*\r
6579     SetWindowPos(hDlg, NULL, rChild.left,\r
6580       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6581       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6582     */\r
6583 \r
6584     /* \r
6585         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6586         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6587         and it doesn't work when you resize the dialog.\r
6588         For now, just give it a default position.\r
6589     */\r
6590     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6591     Translate(hDlg, DLG_Error);\r
6592 \r
6593     errorDialog = hDlg;\r
6594     SetWindowText(hDlg, errorTitle);\r
6595     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6596     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6597     return FALSE;\r
6598 \r
6599   case WM_COMMAND:\r
6600     switch (LOWORD(wParam)) {\r
6601     case IDOK:\r
6602     case IDCANCEL:\r
6603       if (errorDialog == hDlg) errorDialog = NULL;\r
6604       DestroyWindow(hDlg);\r
6605       return TRUE;\r
6606 \r
6607     default:\r
6608       break;\r
6609     }\r
6610     break;\r
6611   }\r
6612   return FALSE;\r
6613 }\r
6614 \r
6615 #ifdef GOTHIC\r
6616 HWND gothicDialog = NULL;\r
6617 \r
6618 LRESULT CALLBACK\r
6619 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6620 {\r
6621   HANDLE hwndText;\r
6622   RECT rChild;\r
6623   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6624 \r
6625   switch (message) {\r
6626   case WM_INITDIALOG:\r
6627     GetWindowRect(hDlg, &rChild);\r
6628 \r
6629     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6630                                                              SWP_NOZORDER);\r
6631 \r
6632     /* \r
6633         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6634         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6635         and it doesn't work when you resize the dialog.\r
6636         For now, just give it a default position.\r
6637     */\r
6638     gothicDialog = hDlg;\r
6639     SetWindowText(hDlg, errorTitle);\r
6640     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6641     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6642     return FALSE;\r
6643 \r
6644   case WM_COMMAND:\r
6645     switch (LOWORD(wParam)) {\r
6646     case IDOK:\r
6647     case IDCANCEL:\r
6648       if (errorDialog == hDlg) errorDialog = NULL;\r
6649       DestroyWindow(hDlg);\r
6650       return TRUE;\r
6651 \r
6652     default:\r
6653       break;\r
6654     }\r
6655     break;\r
6656   }\r
6657   return FALSE;\r
6658 }\r
6659 \r
6660 VOID\r
6661 GothicPopUp(char *title, VariantClass variant)\r
6662 {\r
6663   FARPROC lpProc;\r
6664   static char *lastTitle;\r
6665 \r
6666   strncpy(errorTitle, title, sizeof(errorTitle));\r
6667   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6668 \r
6669   if(lastTitle != title && gothicDialog != NULL) {\r
6670     DestroyWindow(gothicDialog);\r
6671     gothicDialog = NULL;\r
6672   }\r
6673   if(variant != VariantNormal && gothicDialog == NULL) {\r
6674     title = lastTitle;\r
6675     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6676     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6677                  hwndMain, (DLGPROC)lpProc);\r
6678     FreeProcInstance(lpProc);\r
6679   }\r
6680 }\r
6681 #endif\r
6682 \r
6683 /*---------------------------------------------------------------------------*\\r
6684  *\r
6685  *  Ics Interaction console functions\r
6686  *\r
6687 \*---------------------------------------------------------------------------*/\r
6688 \r
6689 #define HISTORY_SIZE 64\r
6690 static char *history[HISTORY_SIZE];\r
6691 int histIn = 0, histP = 0;\r
6692 \r
6693 VOID\r
6694 SaveInHistory(char *cmd)\r
6695 {\r
6696   if (history[histIn] != NULL) {\r
6697     free(history[histIn]);\r
6698     history[histIn] = NULL;\r
6699   }\r
6700   if (*cmd == NULLCHAR) return;\r
6701   history[histIn] = StrSave(cmd);\r
6702   histIn = (histIn + 1) % HISTORY_SIZE;\r
6703   if (history[histIn] != NULL) {\r
6704     free(history[histIn]);\r
6705     history[histIn] = NULL;\r
6706   }\r
6707   histP = histIn;\r
6708 }\r
6709 \r
6710 char *\r
6711 PrevInHistory(char *cmd)\r
6712 {\r
6713   int newhp;\r
6714   if (histP == histIn) {\r
6715     if (history[histIn] != NULL) free(history[histIn]);\r
6716     history[histIn] = StrSave(cmd);\r
6717   }\r
6718   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6719   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6720   histP = newhp;\r
6721   return history[histP];\r
6722 }\r
6723 \r
6724 char *\r
6725 NextInHistory()\r
6726 {\r
6727   if (histP == histIn) return NULL;\r
6728   histP = (histP + 1) % HISTORY_SIZE;\r
6729   return history[histP];   \r
6730 }\r
6731 \r
6732 HMENU\r
6733 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6734 {\r
6735   HMENU hmenu, h;\r
6736   int i = 0;\r
6737   hmenu = LoadMenu(hInst, "TextMenu");\r
6738   h = GetSubMenu(hmenu, 0);\r
6739   while (e->item) {\r
6740     if (strcmp(e->item, "-") == 0) {\r
6741       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6742     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6743       int flags = MF_STRING, j = 0;\r
6744       if (e->item[0] == '|') {\r
6745         flags |= MF_MENUBARBREAK;\r
6746         j++;\r
6747       }\r
6748       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6749       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6750     }\r
6751     e++;\r
6752     i++;\r
6753   } \r
6754   return hmenu;\r
6755 }\r
6756 \r
6757 WNDPROC consoleTextWindowProc;\r
6758 \r
6759 void\r
6760 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6761 {\r
6762   char buf[MSG_SIZ], name[MSG_SIZ];\r
6763   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6764   CHARRANGE sel;\r
6765 \r
6766   if (!getname) {\r
6767     SetWindowText(hInput, command);\r
6768     if (immediate) {\r
6769       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6770     } else {\r
6771       sel.cpMin = 999999;\r
6772       sel.cpMax = 999999;\r
6773       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6774       SetFocus(hInput);\r
6775     }\r
6776     return;\r
6777   }    \r
6778   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6779   if (sel.cpMin == sel.cpMax) {\r
6780     /* Expand to surrounding word */\r
6781     TEXTRANGE tr;\r
6782     do {\r
6783       tr.chrg.cpMax = sel.cpMin;\r
6784       tr.chrg.cpMin = --sel.cpMin;\r
6785       if (sel.cpMin < 0) break;\r
6786       tr.lpstrText = name;\r
6787       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6788     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6789     sel.cpMin++;\r
6790 \r
6791     do {\r
6792       tr.chrg.cpMin = sel.cpMax;\r
6793       tr.chrg.cpMax = ++sel.cpMax;\r
6794       tr.lpstrText = name;\r
6795       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6796     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6797     sel.cpMax--;\r
6798 \r
6799     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6800       MessageBeep(MB_ICONEXCLAMATION);\r
6801       return;\r
6802     }\r
6803     tr.chrg = sel;\r
6804     tr.lpstrText = name;\r
6805     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6806   } else {\r
6807     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6808       MessageBeep(MB_ICONEXCLAMATION);\r
6809       return;\r
6810     }\r
6811     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6812   }\r
6813   if (immediate) {\r
6814     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6815     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6816     SetWindowText(hInput, buf);\r
6817     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6818   } else {\r
6819     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6820       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6821     SetWindowText(hInput, buf);\r
6822     sel.cpMin = 999999;\r
6823     sel.cpMax = 999999;\r
6824     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6825     SetFocus(hInput);\r
6826   }\r
6827 }\r
6828 \r
6829 LRESULT CALLBACK \r
6830 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6831 {\r
6832   HWND hInput;\r
6833   CHARRANGE sel;\r
6834 \r
6835   switch (message) {\r
6836   case WM_KEYDOWN:\r
6837     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6838     if(wParam=='R') return 0;\r
6839     switch (wParam) {\r
6840     case VK_PRIOR:\r
6841       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6842       return 0;\r
6843     case VK_NEXT:\r
6844       sel.cpMin = 999999;\r
6845       sel.cpMax = 999999;\r
6846       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6847       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6848       return 0;\r
6849     }\r
6850     break;\r
6851   case WM_CHAR:\r
6852    if(wParam != '\022') {\r
6853     if (wParam == '\t') {\r
6854       if (GetKeyState(VK_SHIFT) < 0) {\r
6855         /* shifted */\r
6856         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6857         if (buttonDesc[0].hwnd) {\r
6858           SetFocus(buttonDesc[0].hwnd);\r
6859         } else {\r
6860           SetFocus(hwndMain);\r
6861         }\r
6862       } else {\r
6863         /* unshifted */\r
6864         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6865       }\r
6866     } else {\r
6867       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6868       JAWS_DELETE( SetFocus(hInput); )\r
6869       SendMessage(hInput, message, wParam, lParam);\r
6870     }\r
6871     return 0;\r
6872    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
6873    lParam = -1;\r
6874   case WM_RBUTTONDOWN:\r
6875     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6876       /* Move selection here if it was empty */\r
6877       POINT pt;\r
6878       pt.x = LOWORD(lParam);\r
6879       pt.y = HIWORD(lParam);\r
6880       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6881       if (sel.cpMin == sel.cpMax) {\r
6882         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6883         sel.cpMax = sel.cpMin;\r
6884         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6885       }\r
6886       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6887 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6888       POINT pt;\r
6889       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6890       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6891       if (sel.cpMin == sel.cpMax) {\r
6892         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6893         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6894       }\r
6895       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6896         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6897       }\r
6898       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6899       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6900       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6901       MenuPopup(hwnd, pt, hmenu, -1);\r
6902 }\r
6903     }\r
6904     return 0;\r
6905   case WM_RBUTTONUP:\r
6906     if (GetKeyState(VK_SHIFT) & ~1) {\r
6907       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6908         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6909     }\r
6910     return 0;\r
6911   case WM_PASTE:\r
6912     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6913     SetFocus(hInput);\r
6914     return SendMessage(hInput, message, wParam, lParam);\r
6915   case WM_MBUTTONDOWN:\r
6916     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6917   case WM_COMMAND:\r
6918     switch (LOWORD(wParam)) {\r
6919     case IDM_QuickPaste:\r
6920       {\r
6921         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6922         if (sel.cpMin == sel.cpMax) {\r
6923           MessageBeep(MB_ICONEXCLAMATION);\r
6924           return 0;\r
6925         }\r
6926         SendMessage(hwnd, WM_COPY, 0, 0);\r
6927         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6928         SendMessage(hInput, WM_PASTE, 0, 0);\r
6929         SetFocus(hInput);\r
6930         return 0;\r
6931       }\r
6932     case IDM_Cut:\r
6933       SendMessage(hwnd, WM_CUT, 0, 0);\r
6934       return 0;\r
6935     case IDM_Paste:\r
6936       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6937       return 0;\r
6938     case IDM_Copy:\r
6939       SendMessage(hwnd, WM_COPY, 0, 0);\r
6940       return 0;\r
6941     default:\r
6942       {\r
6943         int i = LOWORD(wParam) - IDM_CommandX;\r
6944         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6945             icsTextMenuEntry[i].command != NULL) {\r
6946           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6947                    icsTextMenuEntry[i].getname,\r
6948                    icsTextMenuEntry[i].immediate);\r
6949           return 0;\r
6950         }\r
6951       }\r
6952       break;\r
6953     }\r
6954     break;\r
6955   }\r
6956   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
6957 }\r
6958 \r
6959 WNDPROC consoleInputWindowProc;\r
6960 \r
6961 LRESULT CALLBACK\r
6962 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6963 {\r
6964   char buf[MSG_SIZ];\r
6965   char *p;\r
6966   static BOOL sendNextChar = FALSE;\r
6967   static BOOL quoteNextChar = FALSE;\r
6968   InputSource *is = consoleInputSource;\r
6969   CHARFORMAT cf;\r
6970   CHARRANGE sel;\r
6971 \r
6972   switch (message) {\r
6973   case WM_CHAR:\r
6974     if (!appData.localLineEditing || sendNextChar) {\r
6975       is->buf[0] = (CHAR) wParam;\r
6976       is->count = 1;\r
6977       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6978       sendNextChar = FALSE;\r
6979       return 0;\r
6980     }\r
6981     if (quoteNextChar) {\r
6982       buf[0] = (char) wParam;\r
6983       buf[1] = NULLCHAR;\r
6984       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
6985       quoteNextChar = FALSE;\r
6986       return 0;\r
6987     }\r
6988     switch (wParam) {\r
6989     case '\r':   /* Enter key */\r
6990       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
6991       if (consoleEcho) SaveInHistory(is->buf);\r
6992       is->buf[is->count++] = '\n';\r
6993       is->buf[is->count] = NULLCHAR;\r
6994       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6995       if (consoleEcho) {\r
6996         ConsoleOutput(is->buf, is->count, TRUE);\r
6997       } else if (appData.localLineEditing) {\r
6998         ConsoleOutput("\n", 1, TRUE);\r
6999       }\r
7000       /* fall thru */\r
7001     case '\033': /* Escape key */\r
7002       SetWindowText(hwnd, "");\r
7003       cf.cbSize = sizeof(CHARFORMAT);\r
7004       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7005       if (consoleEcho) {\r
7006         cf.crTextColor = textAttribs[ColorNormal].color;\r
7007       } else {\r
7008         cf.crTextColor = COLOR_ECHOOFF;\r
7009       }\r
7010       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7011       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7012       return 0;\r
7013     case '\t':   /* Tab key */\r
7014       if (GetKeyState(VK_SHIFT) < 0) {\r
7015         /* shifted */\r
7016         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7017       } else {\r
7018         /* unshifted */\r
7019         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7020         if (buttonDesc[0].hwnd) {\r
7021           SetFocus(buttonDesc[0].hwnd);\r
7022         } else {\r
7023           SetFocus(hwndMain);\r
7024         }\r
7025       }\r
7026       return 0;\r
7027     case '\023': /* Ctrl+S */\r
7028       sendNextChar = TRUE;\r
7029       return 0;\r
7030     case '\021': /* Ctrl+Q */\r
7031       quoteNextChar = TRUE;\r
7032       return 0;\r
7033     JAWS_REPLAY\r
7034     default:\r
7035       break;\r
7036     }\r
7037     break;\r
7038   case WM_KEYDOWN:\r
7039     switch (wParam) {\r
7040     case VK_UP:\r
7041       GetWindowText(hwnd, buf, MSG_SIZ);\r
7042       p = PrevInHistory(buf);\r
7043       if (p != NULL) {\r
7044         SetWindowText(hwnd, p);\r
7045         sel.cpMin = 999999;\r
7046         sel.cpMax = 999999;\r
7047         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7048         return 0;\r
7049       }\r
7050       break;\r
7051     case VK_DOWN:\r
7052       p = NextInHistory();\r
7053       if (p != NULL) {\r
7054         SetWindowText(hwnd, p);\r
7055         sel.cpMin = 999999;\r
7056         sel.cpMax = 999999;\r
7057         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7058         return 0;\r
7059       }\r
7060       break;\r
7061     case VK_HOME:\r
7062     case VK_END:\r
7063       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7064       /* fall thru */\r
7065     case VK_PRIOR:\r
7066     case VK_NEXT:\r
7067       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7068       return 0;\r
7069     }\r
7070     break;\r
7071   case WM_MBUTTONDOWN:\r
7072     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7073       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7074     break;\r
7075   case WM_RBUTTONUP:\r
7076     if (GetKeyState(VK_SHIFT) & ~1) {\r
7077       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7078         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7079     } else {\r
7080       POINT pt;\r
7081       HMENU hmenu;\r
7082       hmenu = LoadMenu(hInst, "InputMenu");\r
7083       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7084       if (sel.cpMin == sel.cpMax) {\r
7085         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7086         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7087       }\r
7088       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7089         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7090       }\r
7091       pt.x = LOWORD(lParam);\r
7092       pt.y = HIWORD(lParam);\r
7093       MenuPopup(hwnd, pt, hmenu, -1);\r
7094     }\r
7095     return 0;\r
7096   case WM_COMMAND:\r
7097     switch (LOWORD(wParam)) { \r
7098     case IDM_Undo:\r
7099       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7100       return 0;\r
7101     case IDM_SelectAll:\r
7102       sel.cpMin = 0;\r
7103       sel.cpMax = -1; /*999999?*/\r
7104       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7105       return 0;\r
7106     case IDM_Cut:\r
7107       SendMessage(hwnd, WM_CUT, 0, 0);\r
7108       return 0;\r
7109     case IDM_Paste:\r
7110       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7111       return 0;\r
7112     case IDM_Copy:\r
7113       SendMessage(hwnd, WM_COPY, 0, 0);\r
7114       return 0;\r
7115     }\r
7116     break;\r
7117   }\r
7118   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7119 }\r
7120 \r
7121 #define CO_MAX  100000\r
7122 #define CO_TRIM   1000\r
7123 \r
7124 LRESULT CALLBACK\r
7125 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7126 {\r
7127   static SnapData sd;\r
7128   HWND hText, hInput;\r
7129   RECT rect;\r
7130   static int sizeX, sizeY;\r
7131   int newSizeX, newSizeY;\r
7132   MINMAXINFO *mmi;\r
7133   WORD wMask;\r
7134 \r
7135   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7136   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7137 \r
7138   switch (message) {\r
7139   case WM_NOTIFY:\r
7140     if (((NMHDR*)lParam)->code == EN_LINK)\r
7141     {\r
7142       ENLINK *pLink = (ENLINK*)lParam;\r
7143       if (pLink->msg == WM_LBUTTONUP)\r
7144       {\r
7145         TEXTRANGE tr;\r
7146 \r
7147         tr.chrg = pLink->chrg;\r
7148         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7149         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7150         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7151         free(tr.lpstrText);\r
7152       }\r
7153     }\r
7154     break;\r
7155   case WM_INITDIALOG: /* message: initialize dialog box */\r
7156     hwndConsole = hDlg;\r
7157     SetFocus(hInput);\r
7158     consoleTextWindowProc = (WNDPROC)\r
7159       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7160     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7161     consoleInputWindowProc = (WNDPROC)\r
7162       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7163     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7164     Colorize(ColorNormal, TRUE);\r
7165     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7166     ChangedConsoleFont();\r
7167     GetClientRect(hDlg, &rect);\r
7168     sizeX = rect.right;\r
7169     sizeY = rect.bottom;\r
7170     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7171         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7172       WINDOWPLACEMENT wp;\r
7173       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7174       wp.length = sizeof(WINDOWPLACEMENT);\r
7175       wp.flags = 0;\r
7176       wp.showCmd = SW_SHOW;\r
7177       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7178       wp.rcNormalPosition.left = wpConsole.x;\r
7179       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7180       wp.rcNormalPosition.top = wpConsole.y;\r
7181       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7182       SetWindowPlacement(hDlg, &wp);\r
7183     }\r
7184 \r
7185    // [HGM] Chessknight's change 2004-07-13\r
7186    else { /* Determine Defaults */\r
7187        WINDOWPLACEMENT wp;\r
7188        wpConsole.x = wpMain.width + 1;\r
7189        wpConsole.y = wpMain.y;\r
7190        wpConsole.width = screenWidth -  wpMain.width;\r
7191        wpConsole.height = wpMain.height;\r
7192        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7193        wp.length = sizeof(WINDOWPLACEMENT);\r
7194        wp.flags = 0;\r
7195        wp.showCmd = SW_SHOW;\r
7196        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7197        wp.rcNormalPosition.left = wpConsole.x;\r
7198        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7199        wp.rcNormalPosition.top = wpConsole.y;\r
7200        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7201        SetWindowPlacement(hDlg, &wp);\r
7202     }\r
7203 \r
7204    // Allow hText to highlight URLs and send notifications on them\r
7205    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7206    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7207    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7208    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7209 \r
7210     return FALSE;\r
7211 \r
7212   case WM_SETFOCUS:\r
7213     SetFocus(hInput);\r
7214     return 0;\r
7215 \r
7216   case WM_CLOSE:\r
7217     ExitEvent(0);\r
7218     /* not reached */\r
7219     break;\r
7220 \r
7221   case WM_SIZE:\r
7222     if (IsIconic(hDlg)) break;\r
7223     newSizeX = LOWORD(lParam);\r
7224     newSizeY = HIWORD(lParam);\r
7225     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7226       RECT rectText, rectInput;\r
7227       POINT pt;\r
7228       int newTextHeight, newTextWidth;\r
7229       GetWindowRect(hText, &rectText);\r
7230       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7231       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7232       if (newTextHeight < 0) {\r
7233         newSizeY += -newTextHeight;\r
7234         newTextHeight = 0;\r
7235       }\r
7236       SetWindowPos(hText, NULL, 0, 0,\r
7237         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7238       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7239       pt.x = rectInput.left;\r
7240       pt.y = rectInput.top + newSizeY - sizeY;\r
7241       ScreenToClient(hDlg, &pt);\r
7242       SetWindowPos(hInput, NULL, \r
7243         pt.x, pt.y, /* needs client coords */   \r
7244         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7245         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7246     }\r
7247     sizeX = newSizeX;\r
7248     sizeY = newSizeY;\r
7249     break;\r
7250 \r
7251   case WM_GETMINMAXINFO:\r
7252     /* Prevent resizing window too small */\r
7253     mmi = (MINMAXINFO *) lParam;\r
7254     mmi->ptMinTrackSize.x = 100;\r
7255     mmi->ptMinTrackSize.y = 100;\r
7256     break;\r
7257 \r
7258   /* [AS] Snapping */\r
7259   case WM_ENTERSIZEMOVE:\r
7260     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7261 \r
7262   case WM_SIZING:\r
7263     return OnSizing( &sd, hDlg, wParam, lParam );\r
7264 \r
7265   case WM_MOVING:\r
7266     return OnMoving( &sd, hDlg, wParam, lParam );\r
7267 \r
7268   case WM_EXITSIZEMOVE:\r
7269         UpdateICSWidth(hText);\r
7270     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7271   }\r
7272 \r
7273   return DefWindowProc(hDlg, message, wParam, lParam);\r
7274 }\r
7275 \r
7276 \r
7277 VOID\r
7278 ConsoleCreate()\r
7279 {\r
7280   HWND hCons;\r
7281   if (hwndConsole) return;\r
7282   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7283   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7284 }\r
7285 \r
7286 \r
7287 VOID\r
7288 ConsoleOutput(char* data, int length, int forceVisible)\r
7289 {\r
7290   HWND hText;\r
7291   int trim, exlen;\r
7292   char *p, *q;\r
7293   char buf[CO_MAX+1];\r
7294   POINT pEnd;\r
7295   RECT rect;\r
7296   static int delayLF = 0;\r
7297   CHARRANGE savesel, sel;\r
7298 \r
7299   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7300   p = data;\r
7301   q = buf;\r
7302   if (delayLF) {\r
7303     *q++ = '\r';\r
7304     *q++ = '\n';\r
7305     delayLF = 0;\r
7306   }\r
7307   while (length--) {\r
7308     if (*p == '\n') {\r
7309       if (*++p) {\r
7310         *q++ = '\r';\r
7311         *q++ = '\n';\r
7312       } else {\r
7313         delayLF = 1;\r
7314       }\r
7315     } else if (*p == '\007') {\r
7316        MyPlaySound(&sounds[(int)SoundBell]);\r
7317        p++;\r
7318     } else {\r
7319       *q++ = *p++;\r
7320     }\r
7321   }\r
7322   *q = NULLCHAR;\r
7323   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7324   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7325   /* Save current selection */\r
7326   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7327   exlen = GetWindowTextLength(hText);\r
7328   /* Find out whether current end of text is visible */\r
7329   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7330   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7331   /* Trim existing text if it's too long */\r
7332   if (exlen + (q - buf) > CO_MAX) {\r
7333     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7334     sel.cpMin = 0;\r
7335     sel.cpMax = trim;\r
7336     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7337     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7338     exlen -= trim;\r
7339     savesel.cpMin -= trim;\r
7340     savesel.cpMax -= trim;\r
7341     if (exlen < 0) exlen = 0;\r
7342     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7343     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7344   }\r
7345   /* Append the new text */\r
7346   sel.cpMin = exlen;\r
7347   sel.cpMax = exlen;\r
7348   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7349   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7350   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7351   if (forceVisible || exlen == 0 ||\r
7352       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7353        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7354     /* Scroll to make new end of text visible if old end of text\r
7355        was visible or new text is an echo of user typein */\r
7356     sel.cpMin = 9999999;\r
7357     sel.cpMax = 9999999;\r
7358     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7359     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7360     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7361     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7362   }\r
7363   if (savesel.cpMax == exlen || forceVisible) {\r
7364     /* Move insert point to new end of text if it was at the old\r
7365        end of text or if the new text is an echo of user typein */\r
7366     sel.cpMin = 9999999;\r
7367     sel.cpMax = 9999999;\r
7368     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7369   } else {\r
7370     /* Restore previous selection */\r
7371     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7372   }\r
7373   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7374 }\r
7375 \r
7376 /*---------*/\r
7377 \r
7378 \r
7379 void\r
7380 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7381 {\r
7382   char buf[100];\r
7383   char *str;\r
7384   COLORREF oldFg, oldBg;\r
7385   HFONT oldFont;\r
7386   RECT rect;\r
7387 \r
7388   if(copyNumber > 1)
7389     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7390 \r
7391   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7392   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7393   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7394 \r
7395   rect.left = x;\r
7396   rect.right = x + squareSize;\r
7397   rect.top  = y;\r
7398   rect.bottom = y + squareSize;\r
7399   str = buf;\r
7400 \r
7401   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7402                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7403              y, ETO_CLIPPED|ETO_OPAQUE,\r
7404              &rect, str, strlen(str), NULL);\r
7405 \r
7406   (void) SetTextColor(hdc, oldFg);\r
7407   (void) SetBkColor(hdc, oldBg);\r
7408   (void) SelectObject(hdc, oldFont);\r
7409 }\r
7410 \r
7411 void\r
7412 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7413               RECT *rect, char *color, char *flagFell)\r
7414 {\r
7415   char buf[100];\r
7416   char *str;\r
7417   COLORREF oldFg, oldBg;\r
7418   HFONT oldFont;\r
7419 \r
7420   if (appData.clockMode) {\r
7421     if (tinyLayout)\r
7422       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7423     else\r
7424       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7425     str = buf;\r
7426   } else {\r
7427     str = color;\r
7428   }\r
7429 \r
7430   if (highlight) {\r
7431     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7432     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7433   } else {\r
7434     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7435     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7436   }\r
7437   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7438 \r
7439   JAWS_SILENCE\r
7440 \r
7441   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7442              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7443              rect, str, strlen(str), NULL);\r
7444   if(logoHeight > 0 && appData.clockMode) {\r
7445       RECT r;\r
7446       str += strlen(color)+2;\r
7447       r.top = rect->top + logoHeight/2;\r
7448       r.left = rect->left;\r
7449       r.right = rect->right;\r
7450       r.bottom = rect->bottom;\r
7451       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7452                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7453                  &r, str, strlen(str), NULL);\r
7454   }\r
7455   (void) SetTextColor(hdc, oldFg);\r
7456   (void) SetBkColor(hdc, oldBg);\r
7457   (void) SelectObject(hdc, oldFont);\r
7458 }\r
7459 \r
7460 \r
7461 int\r
7462 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7463            OVERLAPPED *ovl)\r
7464 {\r
7465   int ok, err;\r
7466 \r
7467   /* [AS]  */\r
7468   if( count <= 0 ) {\r
7469     if (appData.debugMode) {\r
7470       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7471     }\r
7472 \r
7473     return ERROR_INVALID_USER_BUFFER;\r
7474   }\r
7475 \r
7476   ResetEvent(ovl->hEvent);\r
7477   ovl->Offset = ovl->OffsetHigh = 0;\r
7478   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7479   if (ok) {\r
7480     err = NO_ERROR;\r
7481   } else {\r
7482     err = GetLastError();\r
7483     if (err == ERROR_IO_PENDING) {\r
7484       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7485       if (ok)\r
7486         err = NO_ERROR;\r
7487       else\r
7488         err = GetLastError();\r
7489     }\r
7490   }\r
7491   return err;\r
7492 }\r
7493 \r
7494 int\r
7495 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7496             OVERLAPPED *ovl)\r
7497 {\r
7498   int ok, err;\r
7499 \r
7500   ResetEvent(ovl->hEvent);\r
7501   ovl->Offset = ovl->OffsetHigh = 0;\r
7502   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7503   if (ok) {\r
7504     err = NO_ERROR;\r
7505   } else {\r
7506     err = GetLastError();\r
7507     if (err == ERROR_IO_PENDING) {\r
7508       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7509       if (ok)\r
7510         err = NO_ERROR;\r
7511       else\r
7512         err = GetLastError();\r
7513     }\r
7514   }\r
7515   return err;\r
7516 }\r
7517 \r
7518 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7519 void CheckForInputBufferFull( InputSource * is )\r
7520 {\r
7521     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7522         /* Look for end of line */\r
7523         char * p = is->buf;\r
7524         \r
7525         while( p < is->next && *p != '\n' ) {\r
7526             p++;\r
7527         }\r
7528 \r
7529         if( p >= is->next ) {\r
7530             if (appData.debugMode) {\r
7531                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7532             }\r
7533 \r
7534             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7535             is->count = (DWORD) -1;\r
7536             is->next = is->buf;\r
7537         }\r
7538     }\r
7539 }\r
7540 \r
7541 DWORD\r
7542 InputThread(LPVOID arg)\r
7543 {\r
7544   InputSource *is;\r
7545   OVERLAPPED ovl;\r
7546 \r
7547   is = (InputSource *) arg;\r
7548   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7549   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7550   while (is->hThread != NULL) {\r
7551     is->error = DoReadFile(is->hFile, is->next,\r
7552                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7553                            &is->count, &ovl);\r
7554     if (is->error == NO_ERROR) {\r
7555       is->next += is->count;\r
7556     } else {\r
7557       if (is->error == ERROR_BROKEN_PIPE) {\r
7558         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7559         is->count = 0;\r
7560       } else {\r
7561         is->count = (DWORD) -1;\r
7562         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7563         break; \r
7564       }\r
7565     }\r
7566 \r
7567     CheckForInputBufferFull( is );\r
7568 \r
7569     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7570 \r
7571     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7572 \r
7573     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7574   }\r
7575 \r
7576   CloseHandle(ovl.hEvent);\r
7577   CloseHandle(is->hFile);\r
7578 \r
7579   if (appData.debugMode) {\r
7580     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7581   }\r
7582 \r
7583   return 0;\r
7584 }\r
7585 \r
7586 \r
7587 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7588 DWORD\r
7589 NonOvlInputThread(LPVOID arg)\r
7590 {\r
7591   InputSource *is;\r
7592   char *p, *q;\r
7593   int i;\r
7594   char prev;\r
7595 \r
7596   is = (InputSource *) arg;\r
7597   while (is->hThread != NULL) {\r
7598     is->error = ReadFile(is->hFile, is->next,\r
7599                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7600                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7601     if (is->error == NO_ERROR) {\r
7602       /* Change CRLF to LF */\r
7603       if (is->next > is->buf) {\r
7604         p = is->next - 1;\r
7605         i = is->count + 1;\r
7606       } else {\r
7607         p = is->next;\r
7608         i = is->count;\r
7609       }\r
7610       q = p;\r
7611       prev = NULLCHAR;\r
7612       while (i > 0) {\r
7613         if (prev == '\r' && *p == '\n') {\r
7614           *(q-1) = '\n';\r
7615           is->count--;\r
7616         } else { \r
7617           *q++ = *p;\r
7618         }\r
7619         prev = *p++;\r
7620         i--;\r
7621       }\r
7622       *q = NULLCHAR;\r
7623       is->next = q;\r
7624     } else {\r
7625       if (is->error == ERROR_BROKEN_PIPE) {\r
7626         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7627         is->count = 0; \r
7628       } else {\r
7629         is->count = (DWORD) -1;\r
7630       }\r
7631     }\r
7632 \r
7633     CheckForInputBufferFull( is );\r
7634 \r
7635     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7636 \r
7637     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7638 \r
7639     if (is->count < 0) break;  /* Quit on error */\r
7640   }\r
7641   CloseHandle(is->hFile);\r
7642   return 0;\r
7643 }\r
7644 \r
7645 DWORD\r
7646 SocketInputThread(LPVOID arg)\r
7647 {\r
7648   InputSource *is;\r
7649 \r
7650   is = (InputSource *) arg;\r
7651   while (is->hThread != NULL) {\r
7652     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7653     if ((int)is->count == SOCKET_ERROR) {\r
7654       is->count = (DWORD) -1;\r
7655       is->error = WSAGetLastError();\r
7656     } else {\r
7657       is->error = NO_ERROR;\r
7658       is->next += is->count;\r
7659       if (is->count == 0 && is->second == is) {\r
7660         /* End of file on stderr; quit with no message */\r
7661         break;\r
7662       }\r
7663     }\r
7664     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7665 \r
7666     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7667 \r
7668     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7669   }\r
7670   return 0;\r
7671 }\r
7672 \r
7673 VOID\r
7674 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7675 {\r
7676   InputSource *is;\r
7677 \r
7678   is = (InputSource *) lParam;\r
7679   if (is->lineByLine) {\r
7680     /* Feed in lines one by one */\r
7681     char *p = is->buf;\r
7682     char *q = p;\r
7683     while (q < is->next) {\r
7684       if (*q++ == '\n') {\r
7685         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7686         p = q;\r
7687       }\r
7688     }\r
7689     \r
7690     /* Move any partial line to the start of the buffer */\r
7691     q = is->buf;\r
7692     while (p < is->next) {\r
7693       *q++ = *p++;\r
7694     }\r
7695     is->next = q;\r
7696 \r
7697     if (is->error != NO_ERROR || is->count == 0) {\r
7698       /* Notify backend of the error.  Note: If there was a partial\r
7699          line at the end, it is not flushed through. */\r
7700       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7701     }\r
7702   } else {\r
7703     /* Feed in the whole chunk of input at once */\r
7704     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7705     is->next = is->buf;\r
7706   }\r
7707 }\r
7708 \r
7709 /*---------------------------------------------------------------------------*\\r
7710  *\r
7711  *  Menu enables. Used when setting various modes.\r
7712  *\r
7713 \*---------------------------------------------------------------------------*/\r
7714 \r
7715 typedef struct {\r
7716   int item;\r
7717   int flags;\r
7718 } Enables;\r
7719 \r
7720 VOID\r
7721 GreyRevert(Boolean grey)\r
7722 { // [HGM] vari: for retracting variations in local mode\r
7723   HMENU hmenu = GetMenu(hwndMain);\r
7724   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7725   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7726 }\r
7727 \r
7728 VOID\r
7729 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7730 {\r
7731   while (enab->item > 0) {\r
7732     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7733     enab++;\r
7734   }\r
7735 }\r
7736 \r
7737 Enables gnuEnables[] = {\r
7738   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7739   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7740   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7741   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7742   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7743   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7744   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7745   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7746   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7747   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7748   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7749   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7750   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7751   { -1, -1 }\r
7752 };\r
7753 \r
7754 Enables icsEnables[] = {\r
7755   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7756   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7757   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7758   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7759   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7760   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7761   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7762   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7763   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7764   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7765   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7766   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7767   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7768   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7769   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7770   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7771   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7772   { -1, -1 }\r
7773 };\r
7774 \r
7775 #if ZIPPY\r
7776 Enables zippyEnables[] = {\r
7777   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7778   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7779   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7780   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7781   { -1, -1 }\r
7782 };\r
7783 #endif\r
7784 \r
7785 Enables ncpEnables[] = {\r
7786   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7787   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7788   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7789   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7790   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7791   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7792   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7793   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7794   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7795   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7796   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7797   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7798   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7799   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7800   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7801   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7802   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7803   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7804   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7805   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7806   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7807   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7808   { -1, -1 }\r
7809 };\r
7810 \r
7811 Enables trainingOnEnables[] = {\r
7812   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7813   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7814   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7815   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7816   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7817   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7818   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7819   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7820   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7821   { -1, -1 }\r
7822 };\r
7823 \r
7824 Enables trainingOffEnables[] = {\r
7825   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7826   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7827   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7828   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7829   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7830   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7831   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7832   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7833   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7834   { -1, -1 }\r
7835 };\r
7836 \r
7837 /* These modify either ncpEnables or gnuEnables */\r
7838 Enables cmailEnables[] = {\r
7839   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7840   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7841   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7842   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7843   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7844   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7845   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7846   { -1, -1 }\r
7847 };\r
7848 \r
7849 Enables machineThinkingEnables[] = {\r
7850   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7851   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7852   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7853   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7854   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7855   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7856   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7857   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7858   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7859   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7860   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7861   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7862   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7863 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7864   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7865   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7866   { -1, -1 }\r
7867 };\r
7868 \r
7869 Enables userThinkingEnables[] = {\r
7870   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7871   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7872   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7873   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7874   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7875   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7876   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7877   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7878   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7879   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7880   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7881   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7882   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7883 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7884   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7885   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7886   { -1, -1 }\r
7887 };\r
7888 \r
7889 /*---------------------------------------------------------------------------*\\r
7890  *\r
7891  *  Front-end interface functions exported by XBoard.\r
7892  *  Functions appear in same order as prototypes in frontend.h.\r
7893  * \r
7894 \*---------------------------------------------------------------------------*/\r
7895 VOID\r
7896 CheckMark(UINT item, int state)\r
7897 {\r
7898     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
7899 }\r
7900 \r
7901 VOID\r
7902 ModeHighlight()\r
7903 {\r
7904   static UINT prevChecked = 0;\r
7905   static int prevPausing = 0;\r
7906   UINT nowChecked;\r
7907 \r
7908   if (pausing != prevPausing) {\r
7909     prevPausing = pausing;\r
7910     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7911                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7912     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7913   }\r
7914 \r
7915   switch (gameMode) {\r
7916   case BeginningOfGame:\r
7917     if (appData.icsActive)\r
7918       nowChecked = IDM_IcsClient;\r
7919     else if (appData.noChessProgram)\r
7920       nowChecked = IDM_EditGame;\r
7921     else\r
7922       nowChecked = IDM_MachineBlack;\r
7923     break;\r
7924   case MachinePlaysBlack:\r
7925     nowChecked = IDM_MachineBlack;\r
7926     break;\r
7927   case MachinePlaysWhite:\r
7928     nowChecked = IDM_MachineWhite;\r
7929     break;\r
7930   case TwoMachinesPlay:\r
7931     nowChecked = IDM_TwoMachines;\r
7932     break;\r
7933   case AnalyzeMode:\r
7934     nowChecked = IDM_AnalysisMode;\r
7935     break;\r
7936   case AnalyzeFile:\r
7937     nowChecked = IDM_AnalyzeFile;\r
7938     break;\r
7939   case EditGame:\r
7940     nowChecked = IDM_EditGame;\r
7941     break;\r
7942   case PlayFromGameFile:\r
7943     nowChecked = IDM_LoadGame;\r
7944     break;\r
7945   case EditPosition:\r
7946     nowChecked = IDM_EditPosition;\r
7947     break;\r
7948   case Training:\r
7949     nowChecked = IDM_Training;\r
7950     break;\r
7951   case IcsPlayingWhite:\r
7952   case IcsPlayingBlack:\r
7953   case IcsObserving:\r
7954   case IcsIdle:\r
7955     nowChecked = IDM_IcsClient;\r
7956     break;\r
7957   default:\r
7958   case EndOfGame:\r
7959     nowChecked = 0;\r
7960     break;\r
7961   }\r
7962   CheckMark(prevChecked, MF_UNCHECKED);\r
7963   CheckMark(nowChecked, MF_CHECKED);\r
7964   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
7965 \r
7966   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
7967     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
7968                           MF_BYCOMMAND|MF_ENABLED);\r
7969   } else {\r
7970     (void) EnableMenuItem(GetMenu(hwndMain), \r
7971                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
7972   }\r
7973 \r
7974   prevChecked = nowChecked;\r
7975 \r
7976   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
7977   if (appData.icsActive) {\r
7978        if (appData.icsEngineAnalyze) {\r
7979                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
7980        } else {\r
7981                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
7982        }\r
7983   }\r
7984   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
7985 }\r
7986 \r
7987 VOID\r
7988 SetICSMode()\r
7989 {\r
7990   HMENU hmenu = GetMenu(hwndMain);\r
7991   SetMenuEnables(hmenu, icsEnables);\r
7992   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
7993     MF_BYCOMMAND|MF_ENABLED);\r
7994 #if ZIPPY\r
7995   if (appData.zippyPlay) {\r
7996     SetMenuEnables(hmenu, zippyEnables);\r
7997     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
7998          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7999           MF_BYCOMMAND|MF_ENABLED);\r
8000   }\r
8001 #endif\r
8002 }\r
8003 \r
8004 VOID\r
8005 SetGNUMode()\r
8006 {\r
8007   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8008 }\r
8009 \r
8010 VOID\r
8011 SetNCPMode()\r
8012 {\r
8013   HMENU hmenu = GetMenu(hwndMain);\r
8014   SetMenuEnables(hmenu, ncpEnables);\r
8015     DrawMenuBar(hwndMain);\r
8016 }\r
8017 \r
8018 VOID\r
8019 SetCmailMode()\r
8020 {\r
8021   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8022 }\r
8023 \r
8024 VOID \r
8025 SetTrainingModeOn()\r
8026 {\r
8027   int i;\r
8028   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8029   for (i = 0; i < N_BUTTONS; i++) {\r
8030     if (buttonDesc[i].hwnd != NULL)\r
8031       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8032   }\r
8033   CommentPopDown();\r
8034 }\r
8035 \r
8036 VOID SetTrainingModeOff()\r
8037 {\r
8038   int i;\r
8039   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8040   for (i = 0; i < N_BUTTONS; i++) {\r
8041     if (buttonDesc[i].hwnd != NULL)\r
8042       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8043   }\r
8044 }\r
8045 \r
8046 \r
8047 VOID\r
8048 SetUserThinkingEnables()\r
8049 {\r
8050   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8051 }\r
8052 \r
8053 VOID\r
8054 SetMachineThinkingEnables()\r
8055 {\r
8056   HMENU hMenu = GetMenu(hwndMain);\r
8057   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8058 \r
8059   SetMenuEnables(hMenu, machineThinkingEnables);\r
8060 \r
8061   if (gameMode == MachinePlaysBlack) {\r
8062     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8063   } else if (gameMode == MachinePlaysWhite) {\r
8064     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8065   } else if (gameMode == TwoMachinesPlay) {\r
8066     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8067   }\r
8068 }\r
8069 \r
8070 \r
8071 VOID\r
8072 DisplayTitle(char *str)\r
8073 {\r
8074   char title[MSG_SIZ], *host;\r
8075   if (str[0] != NULLCHAR) {\r
8076     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8077   } else if (appData.icsActive) {\r
8078     if (appData.icsCommPort[0] != NULLCHAR)\r
8079       host = "ICS";\r
8080     else \r
8081       host = appData.icsHost;\r
8082       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8083   } else if (appData.noChessProgram) {\r
8084     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8085   } else {\r
8086     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8087     strcat(title, ": ");\r
8088     strcat(title, first.tidy);\r
8089   }\r
8090   SetWindowText(hwndMain, title);\r
8091 }\r
8092 \r
8093 \r
8094 VOID\r
8095 DisplayMessage(char *str1, char *str2)\r
8096 {\r
8097   HDC hdc;\r
8098   HFONT oldFont;\r
8099   int remain = MESSAGE_TEXT_MAX - 1;\r
8100   int len;\r
8101 \r
8102   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8103   messageText[0] = NULLCHAR;\r
8104   if (*str1) {\r
8105     len = strlen(str1);\r
8106     if (len > remain) len = remain;\r
8107     strncpy(messageText, str1, len);\r
8108     messageText[len] = NULLCHAR;\r
8109     remain -= len;\r
8110   }\r
8111   if (*str2 && remain >= 2) {\r
8112     if (*str1) {\r
8113       strcat(messageText, "  ");\r
8114       remain -= 2;\r
8115     }\r
8116     len = strlen(str2);\r
8117     if (len > remain) len = remain;\r
8118     strncat(messageText, str2, len);\r
8119   }\r
8120   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
8121   safeStrCpy(lastMsg, messageText, MSG_SIZ);
8122 \r
8123   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8124 \r
8125   SAYMACHINEMOVE();\r
8126 \r
8127   hdc = GetDC(hwndMain);\r
8128   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8129   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8130              &messageRect, messageText, strlen(messageText), NULL);\r
8131   (void) SelectObject(hdc, oldFont);\r
8132   (void) ReleaseDC(hwndMain, hdc);\r
8133 }\r
8134 \r
8135 VOID\r
8136 DisplayError(char *str, int error)\r
8137 {\r
8138   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8139   int len;\r
8140 \r
8141   if (error == 0) {\r
8142     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8143   } else {\r
8144     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8145                         NULL, error, LANG_NEUTRAL,\r
8146                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8147     if (len > 0) {\r
8148       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8149     } else {\r
8150       ErrorMap *em = errmap;\r
8151       while (em->err != 0 && em->err != error) em++;\r
8152       if (em->err != 0) {\r
8153         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8154       } else {\r
8155         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8156       }\r
8157     }\r
8158   }\r
8159   \r
8160   ErrorPopUp(_("Error"), buf);\r
8161 }\r
8162 \r
8163 \r
8164 VOID\r
8165 DisplayMoveError(char *str)\r
8166 {\r
8167   fromX = fromY = -1;\r
8168   ClearHighlights();\r
8169   DrawPosition(FALSE, NULL);\r
8170   if (appData.popupMoveErrors) {\r
8171     ErrorPopUp(_("Error"), str);\r
8172   } else {\r
8173     DisplayMessage(str, "");\r
8174     moveErrorMessageUp = TRUE;\r
8175   }\r
8176 }\r
8177 \r
8178 VOID\r
8179 DisplayFatalError(char *str, int error, int exitStatus)\r
8180 {\r
8181   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8182   int len;\r
8183   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8184 \r
8185   if (error != 0) {\r
8186     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8187                         NULL, error, LANG_NEUTRAL,\r
8188                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8189     if (len > 0) {\r
8190       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8191     } else {\r
8192       ErrorMap *em = errmap;\r
8193       while (em->err != 0 && em->err != error) em++;\r
8194       if (em->err != 0) {\r
8195         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8196       } else {\r
8197         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8198       }\r
8199     }\r
8200     str = buf;\r
8201   }\r
8202   if (appData.debugMode) {\r
8203     fprintf(debugFP, "%s: %s\n", label, str);\r
8204   }\r
8205   if (appData.popupExitMessage) {\r
8206     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8207                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8208   }\r
8209   ExitEvent(exitStatus);\r
8210 }\r
8211 \r
8212 \r
8213 VOID\r
8214 DisplayInformation(char *str)\r
8215 {\r
8216   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8217 }\r
8218 \r
8219 \r
8220 VOID\r
8221 DisplayNote(char *str)\r
8222 {\r
8223   ErrorPopUp(_("Note"), str);\r
8224 }\r
8225 \r
8226 \r
8227 typedef struct {\r
8228   char *title, *question, *replyPrefix;\r
8229   ProcRef pr;\r
8230 } QuestionParams;\r
8231 \r
8232 LRESULT CALLBACK\r
8233 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8234 {\r
8235   static QuestionParams *qp;\r
8236   char reply[MSG_SIZ];\r
8237   int len, err;\r
8238 \r
8239   switch (message) {\r
8240   case WM_INITDIALOG:\r
8241     qp = (QuestionParams *) lParam;\r
8242     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8243     Translate(hDlg, DLG_Question);\r
8244     SetWindowText(hDlg, qp->title);\r
8245     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8246     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8247     return FALSE;\r
8248 \r
8249   case WM_COMMAND:\r
8250     switch (LOWORD(wParam)) {\r
8251     case IDOK:\r
8252       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8253       if (*reply) strcat(reply, " ");\r
8254       len = strlen(reply);\r
8255       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8256       strcat(reply, "\n");\r
8257       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8258       EndDialog(hDlg, TRUE);\r
8259       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8260       return TRUE;\r
8261     case IDCANCEL:\r
8262       EndDialog(hDlg, FALSE);\r
8263       return TRUE;\r
8264     default:\r
8265       break;\r
8266     }\r
8267     break;\r
8268   }\r
8269   return FALSE;\r
8270 }\r
8271 \r
8272 VOID\r
8273 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8274 {\r
8275     QuestionParams qp;\r
8276     FARPROC lpProc;\r
8277     \r
8278     qp.title = title;\r
8279     qp.question = question;\r
8280     qp.replyPrefix = replyPrefix;\r
8281     qp.pr = pr;\r
8282     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8283     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8284       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8285     FreeProcInstance(lpProc);\r
8286 }\r
8287 \r
8288 /* [AS] Pick FRC position */\r
8289 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8290 {\r
8291     static int * lpIndexFRC;\r
8292     BOOL index_is_ok;\r
8293     char buf[16];\r
8294 \r
8295     switch( message )\r
8296     {\r
8297     case WM_INITDIALOG:\r
8298         lpIndexFRC = (int *) lParam;\r
8299 \r
8300         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8301         Translate(hDlg, DLG_NewGameFRC);\r
8302 \r
8303         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8304         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8305         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8306         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8307 \r
8308         break;\r
8309 \r
8310     case WM_COMMAND:\r
8311         switch( LOWORD(wParam) ) {\r
8312         case IDOK:\r
8313             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8314             EndDialog( hDlg, 0 );\r
8315             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8316             return TRUE;\r
8317         case IDCANCEL:\r
8318             EndDialog( hDlg, 1 );   \r
8319             return TRUE;\r
8320         case IDC_NFG_Edit:\r
8321             if( HIWORD(wParam) == EN_CHANGE ) {\r
8322                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8323 \r
8324                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8325             }\r
8326             return TRUE;\r
8327         case IDC_NFG_Random:\r
8328           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8329             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8330             return TRUE;\r
8331         }\r
8332 \r
8333         break;\r
8334     }\r
8335 \r
8336     return FALSE;\r
8337 }\r
8338 \r
8339 int NewGameFRC()\r
8340 {\r
8341     int result;\r
8342     int index = appData.defaultFrcPosition;\r
8343     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8344 \r
8345     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8346 \r
8347     if( result == 0 ) {\r
8348         appData.defaultFrcPosition = index;\r
8349     }\r
8350 \r
8351     return result;\r
8352 }\r
8353 \r
8354 /* [AS] Game list options. Refactored by HGM */\r
8355 \r
8356 HWND gameListOptionsDialog;\r
8357 \r
8358 // low-level front-end: clear text edit / list widget\r
8359 void\r
8360 GLT_ClearList()\r
8361 {\r
8362     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8363 }\r
8364 \r
8365 // low-level front-end: clear text edit / list widget\r
8366 void\r
8367 GLT_DeSelectList()\r
8368 {\r
8369     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8370 }\r
8371 \r
8372 // low-level front-end: append line to text edit / list widget\r
8373 void\r
8374 GLT_AddToList( char *name )\r
8375 {\r
8376     if( name != 0 ) {\r
8377             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8378     }\r
8379 }\r
8380 \r
8381 // low-level front-end: get line from text edit / list widget\r
8382 Boolean\r
8383 GLT_GetFromList( int index, char *name )\r
8384 {\r
8385     if( name != 0 ) {\r
8386             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8387                 return TRUE;\r
8388     }\r
8389     return FALSE;\r
8390 }\r
8391 \r
8392 void GLT_MoveSelection( HWND hDlg, int delta )\r
8393 {\r
8394     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8395     int idx2 = idx1 + delta;\r
8396     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8397 \r
8398     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8399         char buf[128];\r
8400 \r
8401         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8402         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8403         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8404         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8405     }\r
8406 }\r
8407 \r
8408 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8409 {\r
8410     switch( message )\r
8411     {\r
8412     case WM_INITDIALOG:\r
8413         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8414         \r
8415         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8416         Translate(hDlg, DLG_GameListOptions);\r
8417 \r
8418         /* Initialize list */\r
8419         GLT_TagsToList( lpUserGLT );\r
8420 \r
8421         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8422 \r
8423         break;\r
8424 \r
8425     case WM_COMMAND:\r
8426         switch( LOWORD(wParam) ) {\r
8427         case IDOK:\r
8428             GLT_ParseList();\r
8429             EndDialog( hDlg, 0 );\r
8430             return TRUE;\r
8431         case IDCANCEL:\r
8432             EndDialog( hDlg, 1 );\r
8433             return TRUE;\r
8434 \r
8435         case IDC_GLT_Default:\r
8436             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8437             return TRUE;\r
8438 \r
8439         case IDC_GLT_Restore:\r
8440             GLT_TagsToList( appData.gameListTags );\r
8441             return TRUE;\r
8442 \r
8443         case IDC_GLT_Up:\r
8444             GLT_MoveSelection( hDlg, -1 );\r
8445             return TRUE;\r
8446 \r
8447         case IDC_GLT_Down:\r
8448             GLT_MoveSelection( hDlg, +1 );\r
8449             return TRUE;\r
8450         }\r
8451 \r
8452         break;\r
8453     }\r
8454 \r
8455     return FALSE;\r
8456 }\r
8457 \r
8458 int GameListOptions()\r
8459 {\r
8460     int result;\r
8461     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8462 \r
8463       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8464 \r
8465     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8466 \r
8467     if( result == 0 ) {\r
8468         /* [AS] Memory leak here! */\r
8469         appData.gameListTags = strdup( lpUserGLT ); \r
8470     }\r
8471 \r
8472     return result;\r
8473 }\r
8474 \r
8475 VOID\r
8476 DisplayIcsInteractionTitle(char *str)\r
8477 {\r
8478   char consoleTitle[MSG_SIZ];\r
8479 \r
8480     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8481   SetWindowText(hwndConsole, consoleTitle);\r
8482 }\r
8483 \r
8484 void\r
8485 DrawPosition(int fullRedraw, Board board)\r
8486 {\r
8487   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8488 }\r
8489 \r
8490 void NotifyFrontendLogin()\r
8491 {\r
8492         if (hwndConsole)\r
8493                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8494 }\r
8495 \r
8496 VOID\r
8497 ResetFrontEnd()\r
8498 {\r
8499   fromX = fromY = -1;\r
8500   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8501     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8502     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8503     dragInfo.lastpos = dragInfo.pos;\r
8504     dragInfo.start.x = dragInfo.start.y = -1;\r
8505     dragInfo.from = dragInfo.start;\r
8506     ReleaseCapture();\r
8507     DrawPosition(TRUE, NULL);\r
8508   }\r
8509   TagsPopDown();\r
8510 }\r
8511 \r
8512 \r
8513 VOID\r
8514 CommentPopUp(char *title, char *str)\r
8515 {\r
8516   HWND hwnd = GetActiveWindow();\r
8517   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8518   SAY(str);\r
8519   SetActiveWindow(hwnd);\r
8520 }\r
8521 \r
8522 VOID\r
8523 CommentPopDown(void)\r
8524 {\r
8525   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8526   if (commentDialog) {\r
8527     ShowWindow(commentDialog, SW_HIDE);\r
8528   }\r
8529   commentUp = FALSE;\r
8530 }\r
8531 \r
8532 VOID\r
8533 EditCommentPopUp(int index, char *title, char *str)\r
8534 {\r
8535   EitherCommentPopUp(index, title, str, TRUE);\r
8536 }\r
8537 \r
8538 \r
8539 VOID\r
8540 RingBell()\r
8541 {\r
8542   MyPlaySound(&sounds[(int)SoundMove]);\r
8543 }\r
8544 \r
8545 VOID PlayIcsWinSound()\r
8546 {\r
8547   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8548 }\r
8549 \r
8550 VOID PlayIcsLossSound()\r
8551 {\r
8552   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8553 }\r
8554 \r
8555 VOID PlayIcsDrawSound()\r
8556 {\r
8557   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8558 }\r
8559 \r
8560 VOID PlayIcsUnfinishedSound()\r
8561 {\r
8562   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8563 }\r
8564 \r
8565 VOID\r
8566 PlayAlarmSound()\r
8567 {\r
8568   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8569 }\r
8570 \r
8571 \r
8572 VOID\r
8573 EchoOn()\r
8574 {\r
8575   HWND hInput;\r
8576   consoleEcho = TRUE;\r
8577   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8578   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8579   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8580 }\r
8581 \r
8582 \r
8583 VOID\r
8584 EchoOff()\r
8585 {\r
8586   CHARFORMAT cf;\r
8587   HWND hInput;\r
8588   consoleEcho = FALSE;\r
8589   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8590   /* This works OK: set text and background both to the same color */\r
8591   cf = consoleCF;\r
8592   cf.crTextColor = COLOR_ECHOOFF;\r
8593   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8594   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8595 }\r
8596 \r
8597 /* No Raw()...? */\r
8598 \r
8599 void Colorize(ColorClass cc, int continuation)\r
8600 {\r
8601   currentColorClass = cc;\r
8602   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8603   consoleCF.crTextColor = textAttribs[cc].color;\r
8604   consoleCF.dwEffects = textAttribs[cc].effects;\r
8605   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8606 }\r
8607 \r
8608 char *\r
8609 UserName()\r
8610 {\r
8611   static char buf[MSG_SIZ];\r
8612   DWORD bufsiz = MSG_SIZ;\r
8613 \r
8614   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8615         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8616   }\r
8617   if (!GetUserName(buf, &bufsiz)) {\r
8618     /*DisplayError("Error getting user name", GetLastError());*/\r
8619     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8620   }\r
8621   return buf;\r
8622 }\r
8623 \r
8624 char *\r
8625 HostName()\r
8626 {\r
8627   static char buf[MSG_SIZ];\r
8628   DWORD bufsiz = MSG_SIZ;\r
8629 \r
8630   if (!GetComputerName(buf, &bufsiz)) {\r
8631     /*DisplayError("Error getting host name", GetLastError());*/\r
8632     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8633   }\r
8634   return buf;\r
8635 }\r
8636 \r
8637 \r
8638 int\r
8639 ClockTimerRunning()\r
8640 {\r
8641   return clockTimerEvent != 0;\r
8642 }\r
8643 \r
8644 int\r
8645 StopClockTimer()\r
8646 {\r
8647   if (clockTimerEvent == 0) return FALSE;\r
8648   KillTimer(hwndMain, clockTimerEvent);\r
8649   clockTimerEvent = 0;\r
8650   return TRUE;\r
8651 }\r
8652 \r
8653 void\r
8654 StartClockTimer(long millisec)\r
8655 {\r
8656   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8657                              (UINT) millisec, NULL);\r
8658 }\r
8659 \r
8660 void\r
8661 DisplayWhiteClock(long timeRemaining, int highlight)\r
8662 {\r
8663   HDC hdc;\r
8664   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8665 \r
8666   if(appData.noGUI) return;\r
8667   hdc = GetDC(hwndMain);\r
8668   if (!IsIconic(hwndMain)) {\r
8669     DisplayAClock(hdc, timeRemaining, highlight, \r
8670                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8671   }\r
8672   if (highlight && iconCurrent == iconBlack) {\r
8673     iconCurrent = iconWhite;\r
8674     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8675     if (IsIconic(hwndMain)) {\r
8676       DrawIcon(hdc, 2, 2, iconCurrent);\r
8677     }\r
8678   }\r
8679   (void) ReleaseDC(hwndMain, hdc);\r
8680   if (hwndConsole)\r
8681     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8682 }\r
8683 \r
8684 void\r
8685 DisplayBlackClock(long timeRemaining, int highlight)\r
8686 {\r
8687   HDC hdc;\r
8688   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8689 \r
8690   if(appData.noGUI) return;\r
8691   hdc = GetDC(hwndMain);\r
8692   if (!IsIconic(hwndMain)) {\r
8693     DisplayAClock(hdc, timeRemaining, highlight, \r
8694                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8695   }\r
8696   if (highlight && iconCurrent == iconWhite) {\r
8697     iconCurrent = iconBlack;\r
8698     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8699     if (IsIconic(hwndMain)) {\r
8700       DrawIcon(hdc, 2, 2, iconCurrent);\r
8701     }\r
8702   }\r
8703   (void) ReleaseDC(hwndMain, hdc);\r
8704   if (hwndConsole)\r
8705     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8706 }\r
8707 \r
8708 \r
8709 int\r
8710 LoadGameTimerRunning()\r
8711 {\r
8712   return loadGameTimerEvent != 0;\r
8713 }\r
8714 \r
8715 int\r
8716 StopLoadGameTimer()\r
8717 {\r
8718   if (loadGameTimerEvent == 0) return FALSE;\r
8719   KillTimer(hwndMain, loadGameTimerEvent);\r
8720   loadGameTimerEvent = 0;\r
8721   return TRUE;\r
8722 }\r
8723 \r
8724 void\r
8725 StartLoadGameTimer(long millisec)\r
8726 {\r
8727   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8728                                 (UINT) millisec, NULL);\r
8729 }\r
8730 \r
8731 void\r
8732 AutoSaveGame()\r
8733 {\r
8734   char *defName;\r
8735   FILE *f;\r
8736   char fileTitle[MSG_SIZ];\r
8737 \r
8738   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8739   f = OpenFileDialog(hwndMain, "a", defName,\r
8740                      appData.oldSaveStyle ? "gam" : "pgn",\r
8741                      GAME_FILT, \r
8742                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8743   if (f != NULL) {\r
8744     SaveGame(f, 0, "");\r
8745     fclose(f);\r
8746   }\r
8747 }\r
8748 \r
8749 \r
8750 void\r
8751 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8752 {\r
8753   if (delayedTimerEvent != 0) {\r
8754     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8755       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8756     }\r
8757     KillTimer(hwndMain, delayedTimerEvent);\r
8758     delayedTimerEvent = 0;\r
8759     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8760     delayedTimerCallback();\r
8761   }\r
8762   delayedTimerCallback = cb;\r
8763   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8764                                 (UINT) millisec, NULL);\r
8765 }\r
8766 \r
8767 DelayedEventCallback\r
8768 GetDelayedEvent()\r
8769 {\r
8770   if (delayedTimerEvent) {\r
8771     return delayedTimerCallback;\r
8772   } else {\r
8773     return NULL;\r
8774   }\r
8775 }\r
8776 \r
8777 void\r
8778 CancelDelayedEvent()\r
8779 {\r
8780   if (delayedTimerEvent) {\r
8781     KillTimer(hwndMain, delayedTimerEvent);\r
8782     delayedTimerEvent = 0;\r
8783   }\r
8784 }\r
8785 \r
8786 DWORD GetWin32Priority(int nice)\r
8787 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8788 /*\r
8789 REALTIME_PRIORITY_CLASS     0x00000100\r
8790 HIGH_PRIORITY_CLASS         0x00000080\r
8791 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8792 NORMAL_PRIORITY_CLASS       0x00000020\r
8793 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8794 IDLE_PRIORITY_CLASS         0x00000040\r
8795 */\r
8796         if (nice < -15) return 0x00000080;\r
8797         if (nice < 0)   return 0x00008000;\r
8798         if (nice == 0)  return 0x00000020;\r
8799         if (nice < 15)  return 0x00004000;\r
8800         return 0x00000040;\r
8801 }\r
8802 \r
8803 /* Start a child process running the given program.\r
8804    The process's standard output can be read from "from", and its\r
8805    standard input can be written to "to".\r
8806    Exit with fatal error if anything goes wrong.\r
8807    Returns an opaque pointer that can be used to destroy the process\r
8808    later.\r
8809 */\r
8810 int\r
8811 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8812 {\r
8813 #define BUFSIZE 4096\r
8814 \r
8815   HANDLE hChildStdinRd, hChildStdinWr,\r
8816     hChildStdoutRd, hChildStdoutWr;\r
8817   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8818   SECURITY_ATTRIBUTES saAttr;\r
8819   BOOL fSuccess;\r
8820   PROCESS_INFORMATION piProcInfo;\r
8821   STARTUPINFO siStartInfo;\r
8822   ChildProc *cp;\r
8823   char buf[MSG_SIZ];\r
8824   DWORD err;\r
8825 \r
8826   if (appData.debugMode) {\r
8827     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8828   }\r
8829 \r
8830   *pr = NoProc;\r
8831 \r
8832   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8833   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8834   saAttr.bInheritHandle = TRUE;\r
8835   saAttr.lpSecurityDescriptor = NULL;\r
8836 \r
8837   /*\r
8838    * The steps for redirecting child's STDOUT:\r
8839    *     1. Create anonymous pipe to be STDOUT for child.\r
8840    *     2. Create a noninheritable duplicate of read handle,\r
8841    *         and close the inheritable read handle.\r
8842    */\r
8843 \r
8844   /* Create a pipe for the child's STDOUT. */\r
8845   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8846     return GetLastError();\r
8847   }\r
8848 \r
8849   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8850   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8851                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8852                              FALSE,     /* not inherited */\r
8853                              DUPLICATE_SAME_ACCESS);\r
8854   if (! fSuccess) {\r
8855     return GetLastError();\r
8856   }\r
8857   CloseHandle(hChildStdoutRd);\r
8858 \r
8859   /*\r
8860    * The steps for redirecting child's STDIN:\r
8861    *     1. Create anonymous pipe to be STDIN for child.\r
8862    *     2. Create a noninheritable duplicate of write handle,\r
8863    *         and close the inheritable write handle.\r
8864    */\r
8865 \r
8866   /* Create a pipe for the child's STDIN. */\r
8867   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8868     return GetLastError();\r
8869   }\r
8870 \r
8871   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8872   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8873                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8874                              FALSE,     /* not inherited */\r
8875                              DUPLICATE_SAME_ACCESS);\r
8876   if (! fSuccess) {\r
8877     return GetLastError();\r
8878   }\r
8879   CloseHandle(hChildStdinWr);\r
8880 \r
8881   /* Arrange to (1) look in dir for the child .exe file, and\r
8882    * (2) have dir be the child's working directory.  Interpret\r
8883    * dir relative to the directory WinBoard loaded from. */\r
8884   GetCurrentDirectory(MSG_SIZ, buf);\r
8885   SetCurrentDirectory(installDir);\r
8886   // kludgey way to update logos in tourney, as long as back-end can't do it\r
8887   if(!strcmp(cmdLine, first.program)) LoadLogo(&first, 0); else\r
8888   if(!strcmp(cmdLine, second.program)) LoadLogo(&second, 1);\r
8889   SetCurrentDirectory(dir);\r
8890 \r
8891   /* Now create the child process. */\r
8892 \r
8893   siStartInfo.cb = sizeof(STARTUPINFO);\r
8894   siStartInfo.lpReserved = NULL;\r
8895   siStartInfo.lpDesktop = NULL;\r
8896   siStartInfo.lpTitle = NULL;\r
8897   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8898   siStartInfo.cbReserved2 = 0;\r
8899   siStartInfo.lpReserved2 = NULL;\r
8900   siStartInfo.hStdInput = hChildStdinRd;\r
8901   siStartInfo.hStdOutput = hChildStdoutWr;\r
8902   siStartInfo.hStdError = hChildStdoutWr;\r
8903 \r
8904   fSuccess = CreateProcess(NULL,\r
8905                            cmdLine,        /* command line */\r
8906                            NULL,           /* process security attributes */\r
8907                            NULL,           /* primary thread security attrs */\r
8908                            TRUE,           /* handles are inherited */\r
8909                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8910                            NULL,           /* use parent's environment */\r
8911                            NULL,\r
8912                            &siStartInfo, /* STARTUPINFO pointer */\r
8913                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
8914 \r
8915   err = GetLastError();\r
8916   SetCurrentDirectory(buf); /* return to prev directory */\r
8917   if (! fSuccess) {\r
8918     return err;\r
8919   }\r
8920 \r
8921   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
8922     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
8923     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
8924   }\r
8925 \r
8926   /* Close the handles we don't need in the parent */\r
8927   CloseHandle(piProcInfo.hThread);\r
8928   CloseHandle(hChildStdinRd);\r
8929   CloseHandle(hChildStdoutWr);\r
8930 \r
8931   /* Prepare return value */\r
8932   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8933   cp->kind = CPReal;\r
8934   cp->hProcess = piProcInfo.hProcess;\r
8935   cp->pid = piProcInfo.dwProcessId;\r
8936   cp->hFrom = hChildStdoutRdDup;\r
8937   cp->hTo = hChildStdinWrDup;\r
8938 \r
8939   *pr = (void *) cp;\r
8940 \r
8941   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
8942      2000 where engines sometimes don't see the initial command(s)\r
8943      from WinBoard and hang.  I don't understand how that can happen,\r
8944      but the Sleep is harmless, so I've put it in.  Others have also\r
8945      reported what may be the same problem, so hopefully this will fix\r
8946      it for them too.  */\r
8947   Sleep(500);\r
8948 \r
8949   return NO_ERROR;\r
8950 }\r
8951 \r
8952 \r
8953 void\r
8954 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
8955 {\r
8956   ChildProc *cp; int result;\r
8957 \r
8958   cp = (ChildProc *) pr;\r
8959   if (cp == NULL) return;\r
8960 \r
8961   switch (cp->kind) {\r
8962   case CPReal:\r
8963     /* TerminateProcess is considered harmful, so... */\r
8964     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
8965     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
8966     /* The following doesn't work because the chess program\r
8967        doesn't "have the same console" as WinBoard.  Maybe\r
8968        we could arrange for this even though neither WinBoard\r
8969        nor the chess program uses a console for stdio? */\r
8970     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
8971 \r
8972     /* [AS] Special termination modes for misbehaving programs... */\r
8973     if( signal == 9 ) { \r
8974         result = TerminateProcess( cp->hProcess, 0 );\r
8975 \r
8976         if ( appData.debugMode) {\r
8977             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
8978         }\r
8979     }\r
8980     else if( signal == 10 ) {\r
8981         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
8982 \r
8983         if( dw != WAIT_OBJECT_0 ) {\r
8984             result = TerminateProcess( cp->hProcess, 0 );\r
8985 \r
8986             if ( appData.debugMode) {\r
8987                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
8988             }\r
8989 \r
8990         }\r
8991     }\r
8992 \r
8993     CloseHandle(cp->hProcess);\r
8994     break;\r
8995 \r
8996   case CPComm:\r
8997     if (cp->hFrom) CloseHandle(cp->hFrom);\r
8998     break;\r
8999 \r
9000   case CPSock:\r
9001     closesocket(cp->sock);\r
9002     WSACleanup();\r
9003     break;\r
9004 \r
9005   case CPRcmd:\r
9006     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9007     closesocket(cp->sock);\r
9008     closesocket(cp->sock2);\r
9009     WSACleanup();\r
9010     break;\r
9011   }\r
9012   free(cp);\r
9013 }\r
9014 \r
9015 void\r
9016 InterruptChildProcess(ProcRef pr)\r
9017 {\r
9018   ChildProc *cp;\r
9019 \r
9020   cp = (ChildProc *) pr;\r
9021   if (cp == NULL) return;\r
9022   switch (cp->kind) {\r
9023   case CPReal:\r
9024     /* The following doesn't work because the chess program\r
9025        doesn't "have the same console" as WinBoard.  Maybe\r
9026        we could arrange for this even though neither WinBoard\r
9027        nor the chess program uses a console for stdio */\r
9028     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9029     break;\r
9030 \r
9031   case CPComm:\r
9032   case CPSock:\r
9033     /* Can't interrupt */\r
9034     break;\r
9035 \r
9036   case CPRcmd:\r
9037     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9038     break;\r
9039   }\r
9040 }\r
9041 \r
9042 \r
9043 int\r
9044 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9045 {\r
9046   char cmdLine[MSG_SIZ];\r
9047 \r
9048   if (port[0] == NULLCHAR) {\r
9049     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9050   } else {\r
9051     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9052   }\r
9053   return StartChildProcess(cmdLine, "", pr);\r
9054 }\r
9055 \r
9056 \r
9057 /* Code to open TCP sockets */\r
9058 \r
9059 int\r
9060 OpenTCP(char *host, char *port, ProcRef *pr)\r
9061 {\r
9062   ChildProc *cp;\r
9063   int err;\r
9064   SOCKET s;\r
9065   struct sockaddr_in sa, mysa;\r
9066   struct hostent FAR *hp;\r
9067   unsigned short uport;\r
9068   WORD wVersionRequested;\r
9069   WSADATA wsaData;\r
9070 \r
9071   /* Initialize socket DLL */\r
9072   wVersionRequested = MAKEWORD(1, 1);\r
9073   err = WSAStartup(wVersionRequested, &wsaData);\r
9074   if (err != 0) return err;\r
9075 \r
9076   /* Make socket */\r
9077   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9078     err = WSAGetLastError();\r
9079     WSACleanup();\r
9080     return err;\r
9081   }\r
9082 \r
9083   /* Bind local address using (mostly) don't-care values.\r
9084    */\r
9085   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9086   mysa.sin_family = AF_INET;\r
9087   mysa.sin_addr.s_addr = INADDR_ANY;\r
9088   uport = (unsigned short) 0;\r
9089   mysa.sin_port = htons(uport);\r
9090   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9091       == SOCKET_ERROR) {\r
9092     err = WSAGetLastError();\r
9093     WSACleanup();\r
9094     return err;\r
9095   }\r
9096 \r
9097   /* Resolve remote host name */\r
9098   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9099   if (!(hp = gethostbyname(host))) {\r
9100     unsigned int b0, b1, b2, b3;\r
9101 \r
9102     err = WSAGetLastError();\r
9103 \r
9104     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9105       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9106       hp->h_addrtype = AF_INET;\r
9107       hp->h_length = 4;\r
9108       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9109       hp->h_addr_list[0] = (char *) malloc(4);\r
9110       hp->h_addr_list[0][0] = (char) b0;\r
9111       hp->h_addr_list[0][1] = (char) b1;\r
9112       hp->h_addr_list[0][2] = (char) b2;\r
9113       hp->h_addr_list[0][3] = (char) b3;\r
9114     } else {\r
9115       WSACleanup();\r
9116       return err;\r
9117     }\r
9118   }\r
9119   sa.sin_family = hp->h_addrtype;\r
9120   uport = (unsigned short) atoi(port);\r
9121   sa.sin_port = htons(uport);\r
9122   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9123 \r
9124   /* Make connection */\r
9125   if (connect(s, (struct sockaddr *) &sa,\r
9126               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9127     err = WSAGetLastError();\r
9128     WSACleanup();\r
9129     return err;\r
9130   }\r
9131 \r
9132   /* Prepare return value */\r
9133   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9134   cp->kind = CPSock;\r
9135   cp->sock = s;\r
9136   *pr = (ProcRef *) cp;\r
9137 \r
9138   return NO_ERROR;\r
9139 }\r
9140 \r
9141 int\r
9142 OpenCommPort(char *name, ProcRef *pr)\r
9143 {\r
9144   HANDLE h;\r
9145   COMMTIMEOUTS ct;\r
9146   ChildProc *cp;\r
9147   char fullname[MSG_SIZ];\r
9148 \r
9149   if (*name != '\\')\r
9150     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9151   else\r
9152     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9153 \r
9154   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9155                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9156   if (h == (HANDLE) -1) {\r
9157     return GetLastError();\r
9158   }\r
9159   hCommPort = h;\r
9160 \r
9161   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9162 \r
9163   /* Accumulate characters until a 100ms pause, then parse */\r
9164   ct.ReadIntervalTimeout = 100;\r
9165   ct.ReadTotalTimeoutMultiplier = 0;\r
9166   ct.ReadTotalTimeoutConstant = 0;\r
9167   ct.WriteTotalTimeoutMultiplier = 0;\r
9168   ct.WriteTotalTimeoutConstant = 0;\r
9169   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9170 \r
9171   /* Prepare return value */\r
9172   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9173   cp->kind = CPComm;\r
9174   cp->hFrom = h;\r
9175   cp->hTo = h;\r
9176   *pr = (ProcRef *) cp;\r
9177 \r
9178   return NO_ERROR;\r
9179 }\r
9180 \r
9181 int\r
9182 OpenLoopback(ProcRef *pr)\r
9183 {\r
9184   DisplayFatalError(_("Not implemented"), 0, 1);\r
9185   return NO_ERROR;\r
9186 }\r
9187 \r
9188 \r
9189 int\r
9190 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9191 {\r
9192   ChildProc *cp;\r
9193   int err;\r
9194   SOCKET s, s2, s3;\r
9195   struct sockaddr_in sa, mysa;\r
9196   struct hostent FAR *hp;\r
9197   unsigned short uport;\r
9198   WORD wVersionRequested;\r
9199   WSADATA wsaData;\r
9200   int fromPort;\r
9201   char stderrPortStr[MSG_SIZ];\r
9202 \r
9203   /* Initialize socket DLL */\r
9204   wVersionRequested = MAKEWORD(1, 1);\r
9205   err = WSAStartup(wVersionRequested, &wsaData);\r
9206   if (err != 0) return err;\r
9207 \r
9208   /* Resolve remote host name */\r
9209   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9210   if (!(hp = gethostbyname(host))) {\r
9211     unsigned int b0, b1, b2, b3;\r
9212 \r
9213     err = WSAGetLastError();\r
9214 \r
9215     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9216       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9217       hp->h_addrtype = AF_INET;\r
9218       hp->h_length = 4;\r
9219       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9220       hp->h_addr_list[0] = (char *) malloc(4);\r
9221       hp->h_addr_list[0][0] = (char) b0;\r
9222       hp->h_addr_list[0][1] = (char) b1;\r
9223       hp->h_addr_list[0][2] = (char) b2;\r
9224       hp->h_addr_list[0][3] = (char) b3;\r
9225     } else {\r
9226       WSACleanup();\r
9227       return err;\r
9228     }\r
9229   }\r
9230   sa.sin_family = hp->h_addrtype;\r
9231   uport = (unsigned short) 514;\r
9232   sa.sin_port = htons(uport);\r
9233   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9234 \r
9235   /* Bind local socket to unused "privileged" port address\r
9236    */\r
9237   s = INVALID_SOCKET;\r
9238   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9239   mysa.sin_family = AF_INET;\r
9240   mysa.sin_addr.s_addr = INADDR_ANY;\r
9241   for (fromPort = 1023;; fromPort--) {\r
9242     if (fromPort < 0) {\r
9243       WSACleanup();\r
9244       return WSAEADDRINUSE;\r
9245     }\r
9246     if (s == INVALID_SOCKET) {\r
9247       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9248         err = WSAGetLastError();\r
9249         WSACleanup();\r
9250         return err;\r
9251       }\r
9252     }\r
9253     uport = (unsigned short) fromPort;\r
9254     mysa.sin_port = htons(uport);\r
9255     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9256         == SOCKET_ERROR) {\r
9257       err = WSAGetLastError();\r
9258       if (err == WSAEADDRINUSE) continue;\r
9259       WSACleanup();\r
9260       return err;\r
9261     }\r
9262     if (connect(s, (struct sockaddr *) &sa,\r
9263       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9264       err = WSAGetLastError();\r
9265       if (err == WSAEADDRINUSE) {\r
9266         closesocket(s);\r
9267         s = -1;\r
9268         continue;\r
9269       }\r
9270       WSACleanup();\r
9271       return err;\r
9272     }\r
9273     break;\r
9274   }\r
9275 \r
9276   /* Bind stderr local socket to unused "privileged" port address\r
9277    */\r
9278   s2 = INVALID_SOCKET;\r
9279   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9280   mysa.sin_family = AF_INET;\r
9281   mysa.sin_addr.s_addr = INADDR_ANY;\r
9282   for (fromPort = 1023;; fromPort--) {\r
9283     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9284     if (fromPort < 0) {\r
9285       (void) closesocket(s);\r
9286       WSACleanup();\r
9287       return WSAEADDRINUSE;\r
9288     }\r
9289     if (s2 == INVALID_SOCKET) {\r
9290       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9291         err = WSAGetLastError();\r
9292         closesocket(s);\r
9293         WSACleanup();\r
9294         return err;\r
9295       }\r
9296     }\r
9297     uport = (unsigned short) fromPort;\r
9298     mysa.sin_port = htons(uport);\r
9299     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9300         == SOCKET_ERROR) {\r
9301       err = WSAGetLastError();\r
9302       if (err == WSAEADDRINUSE) continue;\r
9303       (void) closesocket(s);\r
9304       WSACleanup();\r
9305       return err;\r
9306     }\r
9307     if (listen(s2, 1) == SOCKET_ERROR) {\r
9308       err = WSAGetLastError();\r
9309       if (err == WSAEADDRINUSE) {\r
9310         closesocket(s2);\r
9311         s2 = INVALID_SOCKET;\r
9312         continue;\r
9313       }\r
9314       (void) closesocket(s);\r
9315       (void) closesocket(s2);\r
9316       WSACleanup();\r
9317       return err;\r
9318     }\r
9319     break;\r
9320   }\r
9321   prevStderrPort = fromPort; // remember port used\r
9322   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9323 \r
9324   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9325     err = WSAGetLastError();\r
9326     (void) closesocket(s);\r
9327     (void) closesocket(s2);\r
9328     WSACleanup();\r
9329     return err;\r
9330   }\r
9331 \r
9332   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9333     err = WSAGetLastError();\r
9334     (void) closesocket(s);\r
9335     (void) closesocket(s2);\r
9336     WSACleanup();\r
9337     return err;\r
9338   }\r
9339   if (*user == NULLCHAR) user = UserName();\r
9340   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9341     err = WSAGetLastError();\r
9342     (void) closesocket(s);\r
9343     (void) closesocket(s2);\r
9344     WSACleanup();\r
9345     return err;\r
9346   }\r
9347   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9348     err = WSAGetLastError();\r
9349     (void) closesocket(s);\r
9350     (void) closesocket(s2);\r
9351     WSACleanup();\r
9352     return err;\r
9353   }\r
9354 \r
9355   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9356     err = WSAGetLastError();\r
9357     (void) closesocket(s);\r
9358     (void) closesocket(s2);\r
9359     WSACleanup();\r
9360     return err;\r
9361   }\r
9362   (void) closesocket(s2);  /* Stop listening */\r
9363 \r
9364   /* Prepare return value */\r
9365   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9366   cp->kind = CPRcmd;\r
9367   cp->sock = s;\r
9368   cp->sock2 = s3;\r
9369   *pr = (ProcRef *) cp;\r
9370 \r
9371   return NO_ERROR;\r
9372 }\r
9373 \r
9374 \r
9375 InputSourceRef\r
9376 AddInputSource(ProcRef pr, int lineByLine,\r
9377                InputCallback func, VOIDSTAR closure)\r
9378 {\r
9379   InputSource *is, *is2 = NULL;\r
9380   ChildProc *cp = (ChildProc *) pr;\r
9381 \r
9382   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9383   is->lineByLine = lineByLine;\r
9384   is->func = func;\r
9385   is->closure = closure;\r
9386   is->second = NULL;\r
9387   is->next = is->buf;\r
9388   if (pr == NoProc) {\r
9389     is->kind = CPReal;\r
9390     consoleInputSource = is;\r
9391   } else {\r
9392     is->kind = cp->kind;\r
9393     /* \r
9394         [AS] Try to avoid a race condition if the thread is given control too early:\r
9395         we create all threads suspended so that the is->hThread variable can be\r
9396         safely assigned, then let the threads start with ResumeThread.\r
9397     */\r
9398     switch (cp->kind) {\r
9399     case CPReal:\r
9400       is->hFile = cp->hFrom;\r
9401       cp->hFrom = NULL; /* now owned by InputThread */\r
9402       is->hThread =\r
9403         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9404                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9405       break;\r
9406 \r
9407     case CPComm:\r
9408       is->hFile = cp->hFrom;\r
9409       cp->hFrom = NULL; /* now owned by InputThread */\r
9410       is->hThread =\r
9411         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9412                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9413       break;\r
9414 \r
9415     case CPSock:\r
9416       is->sock = cp->sock;\r
9417       is->hThread =\r
9418         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9419                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9420       break;\r
9421 \r
9422     case CPRcmd:\r
9423       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9424       *is2 = *is;\r
9425       is->sock = cp->sock;\r
9426       is->second = is2;\r
9427       is2->sock = cp->sock2;\r
9428       is2->second = is2;\r
9429       is->hThread =\r
9430         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9431                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9432       is2->hThread =\r
9433         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9434                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9435       break;\r
9436     }\r
9437 \r
9438     if( is->hThread != NULL ) {\r
9439         ResumeThread( is->hThread );\r
9440     }\r
9441 \r
9442     if( is2 != NULL && is2->hThread != NULL ) {\r
9443         ResumeThread( is2->hThread );\r
9444     }\r
9445   }\r
9446 \r
9447   return (InputSourceRef) is;\r
9448 }\r
9449 \r
9450 void\r
9451 RemoveInputSource(InputSourceRef isr)\r
9452 {\r
9453   InputSource *is;\r
9454 \r
9455   is = (InputSource *) isr;\r
9456   is->hThread = NULL;  /* tell thread to stop */\r
9457   CloseHandle(is->hThread);\r
9458   if (is->second != NULL) {\r
9459     is->second->hThread = NULL;\r
9460     CloseHandle(is->second->hThread);\r
9461   }\r
9462 }\r
9463 \r
9464 int no_wrap(char *message, int count)\r
9465 {\r
9466     ConsoleOutput(message, count, FALSE);\r
9467     return count;\r
9468 }\r
9469 \r
9470 int\r
9471 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9472 {\r
9473   DWORD dOutCount;\r
9474   int outCount = SOCKET_ERROR;\r
9475   ChildProc *cp = (ChildProc *) pr;\r
9476   static OVERLAPPED ovl;\r
9477   static int line = 0;\r
9478 \r
9479   if (pr == NoProc)\r
9480   {\r
9481     if (appData.noJoin || !appData.useInternalWrap)\r
9482       return no_wrap(message, count);\r
9483     else\r
9484     {\r
9485       int width = get_term_width();\r
9486       int len = wrap(NULL, message, count, width, &line);\r
9487       char *msg = malloc(len);\r
9488       int dbgchk;\r
9489 \r
9490       if (!msg)\r
9491         return no_wrap(message, count);\r
9492       else\r
9493       {\r
9494         dbgchk = wrap(msg, message, count, width, &line);\r
9495         if (dbgchk != len && appData.debugMode)\r
9496             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9497         ConsoleOutput(msg, len, FALSE);\r
9498         free(msg);\r
9499         return len;\r
9500       }\r
9501     }\r
9502   }\r
9503 \r
9504   if (ovl.hEvent == NULL) {\r
9505     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9506   }\r
9507   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9508 \r
9509   switch (cp->kind) {\r
9510   case CPSock:\r
9511   case CPRcmd:\r
9512     outCount = send(cp->sock, message, count, 0);\r
9513     if (outCount == SOCKET_ERROR) {\r
9514       *outError = WSAGetLastError();\r
9515     } else {\r
9516       *outError = NO_ERROR;\r
9517     }\r
9518     break;\r
9519 \r
9520   case CPReal:\r
9521     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9522                   &dOutCount, NULL)) {\r
9523       *outError = NO_ERROR;\r
9524       outCount = (int) dOutCount;\r
9525     } else {\r
9526       *outError = GetLastError();\r
9527     }\r
9528     break;\r
9529 \r
9530   case CPComm:\r
9531     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9532                             &dOutCount, &ovl);\r
9533     if (*outError == NO_ERROR) {\r
9534       outCount = (int) dOutCount;\r
9535     }\r
9536     break;\r
9537   }\r
9538   return outCount;\r
9539 }\r
9540 \r
9541 int\r
9542 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9543                        long msdelay)\r
9544 {\r
9545   /* Ignore delay, not implemented for WinBoard */\r
9546   return OutputToProcess(pr, message, count, outError);\r
9547 }\r
9548 \r
9549 \r
9550 void\r
9551 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9552                         char *buf, int count, int error)\r
9553 {\r
9554   DisplayFatalError(_("Not implemented"), 0, 1);\r
9555 }\r
9556 \r
9557 /* see wgamelist.c for Game List functions */\r
9558 /* see wedittags.c for Edit Tags functions */\r
9559 \r
9560 \r
9561 VOID\r
9562 ICSInitScript()\r
9563 {\r
9564   FILE *f;\r
9565   char buf[MSG_SIZ];\r
9566   char *dummy;\r
9567 \r
9568   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9569     f = fopen(buf, "r");\r
9570     if (f != NULL) {\r
9571       ProcessICSInitScript(f);\r
9572       fclose(f);\r
9573     }\r
9574   }\r
9575 }\r
9576 \r
9577 \r
9578 VOID\r
9579 StartAnalysisClock()\r
9580 {\r
9581   if (analysisTimerEvent) return;\r
9582   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9583                                         (UINT) 2000, NULL);\r
9584 }\r
9585 \r
9586 VOID\r
9587 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9588 {\r
9589   highlightInfo.sq[0].x = fromX;\r
9590   highlightInfo.sq[0].y = fromY;\r
9591   highlightInfo.sq[1].x = toX;\r
9592   highlightInfo.sq[1].y = toY;\r
9593 }\r
9594 \r
9595 VOID\r
9596 ClearHighlights()\r
9597 {\r
9598   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9599     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9600 }\r
9601 \r
9602 VOID\r
9603 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9604 {\r
9605   premoveHighlightInfo.sq[0].x = fromX;\r
9606   premoveHighlightInfo.sq[0].y = fromY;\r
9607   premoveHighlightInfo.sq[1].x = toX;\r
9608   premoveHighlightInfo.sq[1].y = toY;\r
9609 }\r
9610 \r
9611 VOID\r
9612 ClearPremoveHighlights()\r
9613 {\r
9614   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9615     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9616 }\r
9617 \r
9618 VOID\r
9619 ShutDownFrontEnd()\r
9620 {\r
9621   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9622   DeleteClipboardTempFiles();\r
9623 }\r
9624 \r
9625 void\r
9626 BoardToTop()\r
9627 {\r
9628     if (IsIconic(hwndMain))\r
9629       ShowWindow(hwndMain, SW_RESTORE);\r
9630 \r
9631     SetActiveWindow(hwndMain);\r
9632 }\r
9633 \r
9634 /*\r
9635  * Prototypes for animation support routines\r
9636  */\r
9637 static void ScreenSquare(int column, int row, POINT * pt);\r
9638 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9639      POINT frames[], int * nFrames);\r
9640 \r
9641 \r
9642 #define kFactor 4\r
9643 \r
9644 void\r
9645 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9646 {       // [HGM] atomic: animate blast wave\r
9647         int i;\r
9648 \r
9649         explodeInfo.fromX = fromX;\r
9650         explodeInfo.fromY = fromY;\r
9651         explodeInfo.toX = toX;\r
9652         explodeInfo.toY = toY;\r
9653         for(i=1; i<4*kFactor; i++) {\r
9654             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9655             DrawPosition(FALSE, board);\r
9656             Sleep(appData.animSpeed);\r
9657         }\r
9658         explodeInfo.radius = 0;\r
9659         DrawPosition(TRUE, board);\r
9660 }\r
9661 \r
9662 void\r
9663 AnimateMove(board, fromX, fromY, toX, toY)\r
9664      Board board;\r
9665      int fromX;\r
9666      int fromY;\r
9667      int toX;\r
9668      int toY;\r
9669 {\r
9670   ChessSquare piece;\r
9671   POINT start, finish, mid;\r
9672   POINT frames[kFactor * 2 + 1];\r
9673   int nFrames, n;\r
9674 \r
9675   if (!appData.animate) return;\r
9676   if (doingSizing) return;\r
9677   if (fromY < 0 || fromX < 0) return;\r
9678   piece = board[fromY][fromX];\r
9679   if (piece >= EmptySquare) return;\r
9680 \r
9681   ScreenSquare(fromX, fromY, &start);\r
9682   ScreenSquare(toX, toY, &finish);\r
9683 \r
9684   /* All moves except knight jumps move in straight line */\r
9685   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9686     mid.x = start.x + (finish.x - start.x) / 2;\r
9687     mid.y = start.y + (finish.y - start.y) / 2;\r
9688   } else {\r
9689     /* Knight: make straight movement then diagonal */\r
9690     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9691        mid.x = start.x + (finish.x - start.x) / 2;\r
9692        mid.y = start.y;\r
9693      } else {\r
9694        mid.x = start.x;\r
9695        mid.y = start.y + (finish.y - start.y) / 2;\r
9696      }\r
9697   }\r
9698   \r
9699   /* Don't use as many frames for very short moves */\r
9700   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9701     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9702   else\r
9703     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9704 \r
9705   animInfo.from.x = fromX;\r
9706   animInfo.from.y = fromY;\r
9707   animInfo.to.x = toX;\r
9708   animInfo.to.y = toY;\r
9709   animInfo.lastpos = start;\r
9710   animInfo.piece = piece;\r
9711   for (n = 0; n < nFrames; n++) {\r
9712     animInfo.pos = frames[n];\r
9713     DrawPosition(FALSE, NULL);\r
9714     animInfo.lastpos = animInfo.pos;\r
9715     Sleep(appData.animSpeed);\r
9716   }\r
9717   animInfo.pos = finish;\r
9718   DrawPosition(FALSE, NULL);\r
9719   animInfo.piece = EmptySquare;\r
9720   Explode(board, fromX, fromY, toX, toY);\r
9721 }\r
9722 \r
9723 /*      Convert board position to corner of screen rect and color       */\r
9724 \r
9725 static void\r
9726 ScreenSquare(column, row, pt)\r
9727      int column; int row; POINT * pt;\r
9728 {\r
9729   if (flipView) {\r
9730     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9731     pt->y = lineGap + row * (squareSize + lineGap);\r
9732   } else {\r
9733     pt->x = lineGap + column * (squareSize + lineGap);\r
9734     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9735   }\r
9736 }\r
9737 \r
9738 /*      Generate a series of frame coords from start->mid->finish.\r
9739         The movement rate doubles until the half way point is\r
9740         reached, then halves back down to the final destination,\r
9741         which gives a nice slow in/out effect. The algorithmn\r
9742         may seem to generate too many intermediates for short\r
9743         moves, but remember that the purpose is to attract the\r
9744         viewers attention to the piece about to be moved and\r
9745         then to where it ends up. Too few frames would be less\r
9746         noticeable.                                             */\r
9747 \r
9748 static void\r
9749 Tween(start, mid, finish, factor, frames, nFrames)\r
9750      POINT * start; POINT * mid;\r
9751      POINT * finish; int factor;\r
9752      POINT frames[]; int * nFrames;\r
9753 {\r
9754   int n, fraction = 1, count = 0;\r
9755 \r
9756   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9757   for (n = 0; n < factor; n++)\r
9758     fraction *= 2;\r
9759   for (n = 0; n < factor; n++) {\r
9760     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9761     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9762     count ++;\r
9763     fraction = fraction / 2;\r
9764   }\r
9765   \r
9766   /* Midpoint */\r
9767   frames[count] = *mid;\r
9768   count ++;\r
9769   \r
9770   /* Slow out, stepping 1/2, then 1/4, ... */\r
9771   fraction = 2;\r
9772   for (n = 0; n < factor; n++) {\r
9773     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9774     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9775     count ++;\r
9776     fraction = fraction * 2;\r
9777   }\r
9778   *nFrames = count;\r
9779 }\r
9780 \r
9781 void\r
9782 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9783 {\r
9784     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9785 \r
9786     EvalGraphSet( first, last, current, pvInfoList );\r
9787 }\r
9788 \r
9789 void\r
9790 SettingsPopUp(ChessProgramState *cps)\r
9791 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9792       EngineOptionsPopup(savedHwnd, cps);\r
9793 }\r
9794 \r
9795 int flock(int fid, int code)\r
9796 {\r
9797     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
9798     OVERLAPPED ov;\r
9799     ov.hEvent = NULL;\r
9800     ov.Offset = 0;\r
9801     ov.OffsetHigh = 0;\r
9802     switch(code) {\r
9803       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
9804       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
9805       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
9806       default: return -1;\r
9807     }\r
9808     return 0;\r
9809 }\r