Make option to draw second engine from first list
[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, singleList ? "fd" : "sd", appData.secondDirectory,\r
6075                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\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, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6113         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6114         p = buf;
6115         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6116         ParseArgs(StringGet, &p);\r
6117         SwapEngines(singleList); // ... and then make it 'second'\r
6118         appData.noChessProgram = FALSE;\r
6119         appData.icsActive = FALSE;\r
6120       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6121         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6122         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6123         p = buf;\r
6124         ParseArgs(StringGet, &p);\r
6125         if (appData.zippyPlay) {\r
6126           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6127           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6128           p = buf;\r
6129           ParseArgs(StringGet, &p);\r
6130         }\r
6131       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6132         appData.noChessProgram = TRUE;\r
6133         appData.icsActive = FALSE;\r
6134       } else {\r
6135         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6136                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6137         return TRUE;\r
6138       }\r
6139       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6140         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6141         p = buf;\r
6142         ParseArgs(StringGet, &p);\r
6143       }\r
6144       EndDialog(hDlg, TRUE);\r
6145       return TRUE;\r
6146 \r
6147     case IDCANCEL:\r
6148       ExitEvent(0);\r
6149       return TRUE;\r
6150 \r
6151     case IDM_HELPCONTENTS:\r
6152       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6153         MessageBox (GetFocus(),\r
6154                     _("Unable to activate help"),\r
6155                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6156       }\r
6157       break;\r
6158 \r
6159     default:\r
6160       SetStartupDialogEnables(hDlg);\r
6161       break;\r
6162     }\r
6163     break;\r
6164   }\r
6165   return FALSE;\r
6166 }\r
6167 \r
6168 /*---------------------------------------------------------------------------*\\r
6169  *\r
6170  * About box dialog functions\r
6171  *\r
6172 \*---------------------------------------------------------------------------*/\r
6173 \r
6174 /* Process messages for "About" dialog box */\r
6175 LRESULT CALLBACK\r
6176 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6177 {\r
6178   switch (message) {\r
6179   case WM_INITDIALOG: /* message: initialize dialog box */\r
6180     /* Center the dialog over the application window */\r
6181     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6182     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6183     Translate(hDlg, ABOUTBOX);\r
6184     JAWS_COPYRIGHT\r
6185     return (TRUE);\r
6186 \r
6187   case WM_COMMAND: /* message: received a command */\r
6188     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6189         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6190       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6191       return (TRUE);\r
6192     }\r
6193     break;\r
6194   }\r
6195   return (FALSE);\r
6196 }\r
6197 \r
6198 /*---------------------------------------------------------------------------*\\r
6199  *\r
6200  * Comment Dialog functions\r
6201  *\r
6202 \*---------------------------------------------------------------------------*/\r
6203 \r
6204 LRESULT CALLBACK\r
6205 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6206 {\r
6207   static HANDLE hwndText = NULL;\r
6208   int len, newSizeX, newSizeY, flags;\r
6209   static int sizeX, sizeY;\r
6210   char *str;\r
6211   RECT rect;\r
6212   MINMAXINFO *mmi;\r
6213 \r
6214   switch (message) {\r
6215   case WM_INITDIALOG: /* message: initialize dialog box */\r
6216     /* Initialize the dialog items */\r
6217     Translate(hDlg, DLG_EditComment);\r
6218     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6219     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6220     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6221     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6222     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6223     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6224     SetWindowText(hDlg, commentTitle);\r
6225     if (editComment) {\r
6226       SetFocus(hwndText);\r
6227     } else {\r
6228       SetFocus(GetDlgItem(hDlg, IDOK));\r
6229     }\r
6230     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6231                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6232                 MAKELPARAM(FALSE, 0));\r
6233     /* Size and position the dialog */\r
6234     if (!commentDialog) {\r
6235       commentDialog = hDlg;\r
6236       flags = SWP_NOZORDER;\r
6237       GetClientRect(hDlg, &rect);\r
6238       sizeX = rect.right;\r
6239       sizeY = rect.bottom;\r
6240       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6241           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6242         WINDOWPLACEMENT wp;\r
6243         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6244         wp.length = sizeof(WINDOWPLACEMENT);\r
6245         wp.flags = 0;\r
6246         wp.showCmd = SW_SHOW;\r
6247         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6248         wp.rcNormalPosition.left = wpComment.x;\r
6249         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6250         wp.rcNormalPosition.top = wpComment.y;\r
6251         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6252         SetWindowPlacement(hDlg, &wp);\r
6253 \r
6254         GetClientRect(hDlg, &rect);\r
6255         newSizeX = rect.right;\r
6256         newSizeY = rect.bottom;\r
6257         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6258                               newSizeX, newSizeY);\r
6259         sizeX = newSizeX;\r
6260         sizeY = newSizeY;\r
6261       }\r
6262     }\r
6263     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6264     return FALSE;\r
6265 \r
6266   case WM_COMMAND: /* message: received a command */\r
6267     switch (LOWORD(wParam)) {\r
6268     case IDOK:\r
6269       if (editComment) {\r
6270         char *p, *q;\r
6271         /* Read changed options from the dialog box */\r
6272         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6273         len = GetWindowTextLength(hwndText);\r
6274         str = (char *) malloc(len + 1);\r
6275         GetWindowText(hwndText, str, len + 1);\r
6276         p = q = str;\r
6277         while (*q) {\r
6278           if (*q == '\r')\r
6279             q++;\r
6280           else\r
6281             *p++ = *q++;\r
6282         }\r
6283         *p = NULLCHAR;\r
6284         ReplaceComment(commentIndex, str);\r
6285         free(str);\r
6286       }\r
6287       CommentPopDown();\r
6288       return TRUE;\r
6289 \r
6290     case IDCANCEL:\r
6291     case OPT_CancelComment:\r
6292       CommentPopDown();\r
6293       return TRUE;\r
6294 \r
6295     case OPT_ClearComment:\r
6296       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6297       break;\r
6298 \r
6299     case OPT_EditComment:\r
6300       EditCommentEvent();\r
6301       return TRUE;\r
6302 \r
6303     default:\r
6304       break;\r
6305     }\r
6306     break;\r
6307 \r
6308   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6309         if( wParam == OPT_CommentText ) {\r
6310             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6311 \r
6312             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6313                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6314                 POINTL pt;\r
6315                 LRESULT index;\r
6316 \r
6317                 pt.x = LOWORD( lpMF->lParam );\r
6318                 pt.y = HIWORD( lpMF->lParam );\r
6319 \r
6320                 if(lpMF->msg == WM_CHAR) {\r
6321                         CHARRANGE sel;\r
6322                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6323                         index = sel.cpMin;\r
6324                 } else\r
6325                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6326 \r
6327                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6328                 len = GetWindowTextLength(hwndText);\r
6329                 str = (char *) malloc(len + 1);\r
6330                 GetWindowText(hwndText, str, len + 1);\r
6331                 ReplaceComment(commentIndex, str);\r
6332                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6333                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6334                 free(str);\r
6335 \r
6336                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6337                 lpMF->msg = WM_USER;\r
6338 \r
6339                 return TRUE;\r
6340             }\r
6341         }\r
6342         break;\r
6343 \r
6344   case WM_SIZE:\r
6345     newSizeX = LOWORD(lParam);\r
6346     newSizeY = HIWORD(lParam);\r
6347     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6348     sizeX = newSizeX;\r
6349     sizeY = newSizeY;\r
6350     break;\r
6351 \r
6352   case WM_GETMINMAXINFO:\r
6353     /* Prevent resizing window too small */\r
6354     mmi = (MINMAXINFO *) lParam;\r
6355     mmi->ptMinTrackSize.x = 100;\r
6356     mmi->ptMinTrackSize.y = 100;\r
6357     break;\r
6358   }\r
6359   return FALSE;\r
6360 }\r
6361 \r
6362 VOID\r
6363 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6364 {\r
6365   FARPROC lpProc;\r
6366   char *p, *q;\r
6367 \r
6368   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6369 \r
6370   if (str == NULL) str = "";\r
6371   p = (char *) malloc(2 * strlen(str) + 2);\r
6372   q = p;\r
6373   while (*str) {\r
6374     if (*str == '\n') *q++ = '\r';\r
6375     *q++ = *str++;\r
6376   }\r
6377   *q = NULLCHAR;\r
6378   if (commentText != NULL) free(commentText);\r
6379 \r
6380   commentIndex = index;\r
6381   commentTitle = title;\r
6382   commentText = p;\r
6383   editComment = edit;\r
6384 \r
6385   if (commentDialog) {\r
6386     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6387     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6388   } else {\r
6389     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6390     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6391                  hwndMain, (DLGPROC)lpProc);\r
6392     FreeProcInstance(lpProc);\r
6393   }\r
6394   commentUp = TRUE;\r
6395 }\r
6396 \r
6397 \r
6398 /*---------------------------------------------------------------------------*\\r
6399  *\r
6400  * Type-in move dialog functions\r
6401  * \r
6402 \*---------------------------------------------------------------------------*/\r
6403 \r
6404 LRESULT CALLBACK\r
6405 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6406 {\r
6407   char move[MSG_SIZ];\r
6408   HWND hInput;\r
6409 \r
6410   switch (message) {\r
6411   case WM_INITDIALOG:\r
6412     move[0] = (char) lParam;\r
6413     move[1] = NULLCHAR;\r
6414     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6415     Translate(hDlg, DLG_TypeInMove);\r
6416     hInput = GetDlgItem(hDlg, OPT_Move);\r
6417     SetWindowText(hInput, move);\r
6418     SetFocus(hInput);\r
6419     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6420     return FALSE;\r
6421 \r
6422   case WM_COMMAND:\r
6423     switch (LOWORD(wParam)) {\r
6424     case IDOK:
6425 \r
6426       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6427       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
6428       TypeInDoneEvent(move);\r
6429       EndDialog(hDlg, TRUE);\r
6430       return TRUE;\r
6431     case IDCANCEL:\r
6432       EndDialog(hDlg, FALSE);\r
6433       return TRUE;\r
6434     default:\r
6435       break;\r
6436     }\r
6437     break;\r
6438   }\r
6439   return FALSE;\r
6440 }\r
6441 \r
6442 VOID\r
6443 PopUpMoveDialog(char firstchar)\r
6444 {\r
6445     FARPROC lpProc;\r
6446 \r
6447       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6448       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6449         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6450       FreeProcInstance(lpProc);\r
6451 }\r
6452 \r
6453 /*---------------------------------------------------------------------------*\\r
6454  *\r
6455  * Type-in name dialog functions\r
6456  * \r
6457 \*---------------------------------------------------------------------------*/\r
6458 \r
6459 LRESULT CALLBACK\r
6460 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6461 {\r
6462   char move[MSG_SIZ];\r
6463   HWND hInput;\r
6464 \r
6465   switch (message) {\r
6466   case WM_INITDIALOG:\r
6467     move[0] = (char) lParam;\r
6468     move[1] = NULLCHAR;\r
6469     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6470     Translate(hDlg, DLG_TypeInName);\r
6471     hInput = GetDlgItem(hDlg, OPT_Name);\r
6472     SetWindowText(hInput, move);\r
6473     SetFocus(hInput);\r
6474     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6475     return FALSE;\r
6476 \r
6477   case WM_COMMAND:\r
6478     switch (LOWORD(wParam)) {\r
6479     case IDOK:\r
6480       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6481       appData.userName = strdup(move);\r
6482       SetUserLogo();\r
6483       SetGameInfo();\r
6484       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6485         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6486         DisplayTitle(move);\r
6487       }\r
6488 \r
6489 \r
6490       EndDialog(hDlg, TRUE);\r
6491       return TRUE;\r
6492     case IDCANCEL:\r
6493       EndDialog(hDlg, FALSE);\r
6494       return TRUE;\r
6495     default:\r
6496       break;\r
6497     }\r
6498     break;\r
6499   }\r
6500   return FALSE;\r
6501 }\r
6502 \r
6503 VOID\r
6504 PopUpNameDialog(char firstchar)\r
6505 {\r
6506     FARPROC lpProc;\r
6507     \r
6508       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6509       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6510         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6511       FreeProcInstance(lpProc);\r
6512 }\r
6513 \r
6514 /*---------------------------------------------------------------------------*\\r
6515  *\r
6516  *  Error dialogs\r
6517  * \r
6518 \*---------------------------------------------------------------------------*/\r
6519 \r
6520 /* Nonmodal error box */\r
6521 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6522                              WPARAM wParam, LPARAM lParam);\r
6523 \r
6524 VOID\r
6525 ErrorPopUp(char *title, char *content)\r
6526 {\r
6527   FARPROC lpProc;\r
6528   char *p, *q;\r
6529   BOOLEAN modal = hwndMain == NULL;\r
6530 \r
6531   p = content;\r
6532   q = errorMessage;\r
6533   while (*p) {\r
6534     if (*p == '\n') {\r
6535       if (modal) {\r
6536         *q++ = ' ';\r
6537         p++;\r
6538       } else {\r
6539         *q++ = '\r';\r
6540         *q++ = *p++;\r
6541       }\r
6542     } else {\r
6543       *q++ = *p++;\r
6544     }\r
6545   }\r
6546   *q = NULLCHAR;\r
6547   strncpy(errorTitle, title, sizeof(errorTitle));\r
6548   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6549   \r
6550   if (modal) {\r
6551     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6552   } else {\r
6553     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6554     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6555                  hwndMain, (DLGPROC)lpProc);\r
6556     FreeProcInstance(lpProc);\r
6557   }\r
6558 }\r
6559 \r
6560 VOID\r
6561 ErrorPopDown()\r
6562 {\r
6563   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6564   if (errorDialog == NULL) return;\r
6565   DestroyWindow(errorDialog);\r
6566   errorDialog = NULL;\r
6567   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6568 }\r
6569 \r
6570 LRESULT CALLBACK\r
6571 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6572 {\r
6573   HANDLE hwndText;\r
6574   RECT rChild;\r
6575 \r
6576   switch (message) {\r
6577   case WM_INITDIALOG:\r
6578     GetWindowRect(hDlg, &rChild);\r
6579 \r
6580     /*\r
6581     SetWindowPos(hDlg, NULL, rChild.left,\r
6582       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6583       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6584     */\r
6585 \r
6586     /* \r
6587         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6588         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6589         and it doesn't work when you resize the dialog.\r
6590         For now, just give it a default position.\r
6591     */\r
6592     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6593     Translate(hDlg, DLG_Error);\r
6594 \r
6595     errorDialog = hDlg;\r
6596     SetWindowText(hDlg, errorTitle);\r
6597     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6598     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6599     return FALSE;\r
6600 \r
6601   case WM_COMMAND:\r
6602     switch (LOWORD(wParam)) {\r
6603     case IDOK:\r
6604     case IDCANCEL:\r
6605       if (errorDialog == hDlg) errorDialog = NULL;\r
6606       DestroyWindow(hDlg);\r
6607       return TRUE;\r
6608 \r
6609     default:\r
6610       break;\r
6611     }\r
6612     break;\r
6613   }\r
6614   return FALSE;\r
6615 }\r
6616 \r
6617 #ifdef GOTHIC\r
6618 HWND gothicDialog = NULL;\r
6619 \r
6620 LRESULT CALLBACK\r
6621 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6622 {\r
6623   HANDLE hwndText;\r
6624   RECT rChild;\r
6625   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6626 \r
6627   switch (message) {\r
6628   case WM_INITDIALOG:\r
6629     GetWindowRect(hDlg, &rChild);\r
6630 \r
6631     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6632                                                              SWP_NOZORDER);\r
6633 \r
6634     /* \r
6635         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6636         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6637         and it doesn't work when you resize the dialog.\r
6638         For now, just give it a default position.\r
6639     */\r
6640     gothicDialog = hDlg;\r
6641     SetWindowText(hDlg, errorTitle);\r
6642     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6643     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6644     return FALSE;\r
6645 \r
6646   case WM_COMMAND:\r
6647     switch (LOWORD(wParam)) {\r
6648     case IDOK:\r
6649     case IDCANCEL:\r
6650       if (errorDialog == hDlg) errorDialog = NULL;\r
6651       DestroyWindow(hDlg);\r
6652       return TRUE;\r
6653 \r
6654     default:\r
6655       break;\r
6656     }\r
6657     break;\r
6658   }\r
6659   return FALSE;\r
6660 }\r
6661 \r
6662 VOID\r
6663 GothicPopUp(char *title, VariantClass variant)\r
6664 {\r
6665   FARPROC lpProc;\r
6666   static char *lastTitle;\r
6667 \r
6668   strncpy(errorTitle, title, sizeof(errorTitle));\r
6669   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6670 \r
6671   if(lastTitle != title && gothicDialog != NULL) {\r
6672     DestroyWindow(gothicDialog);\r
6673     gothicDialog = NULL;\r
6674   }\r
6675   if(variant != VariantNormal && gothicDialog == NULL) {\r
6676     title = lastTitle;\r
6677     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6678     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6679                  hwndMain, (DLGPROC)lpProc);\r
6680     FreeProcInstance(lpProc);\r
6681   }\r
6682 }\r
6683 #endif\r
6684 \r
6685 /*---------------------------------------------------------------------------*\\r
6686  *\r
6687  *  Ics Interaction console functions\r
6688  *\r
6689 \*---------------------------------------------------------------------------*/\r
6690 \r
6691 #define HISTORY_SIZE 64\r
6692 static char *history[HISTORY_SIZE];\r
6693 int histIn = 0, histP = 0;\r
6694 \r
6695 VOID\r
6696 SaveInHistory(char *cmd)\r
6697 {\r
6698   if (history[histIn] != NULL) {\r
6699     free(history[histIn]);\r
6700     history[histIn] = NULL;\r
6701   }\r
6702   if (*cmd == NULLCHAR) return;\r
6703   history[histIn] = StrSave(cmd);\r
6704   histIn = (histIn + 1) % HISTORY_SIZE;\r
6705   if (history[histIn] != NULL) {\r
6706     free(history[histIn]);\r
6707     history[histIn] = NULL;\r
6708   }\r
6709   histP = histIn;\r
6710 }\r
6711 \r
6712 char *\r
6713 PrevInHistory(char *cmd)\r
6714 {\r
6715   int newhp;\r
6716   if (histP == histIn) {\r
6717     if (history[histIn] != NULL) free(history[histIn]);\r
6718     history[histIn] = StrSave(cmd);\r
6719   }\r
6720   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6721   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6722   histP = newhp;\r
6723   return history[histP];\r
6724 }\r
6725 \r
6726 char *\r
6727 NextInHistory()\r
6728 {\r
6729   if (histP == histIn) return NULL;\r
6730   histP = (histP + 1) % HISTORY_SIZE;\r
6731   return history[histP];   \r
6732 }\r
6733 \r
6734 HMENU\r
6735 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6736 {\r
6737   HMENU hmenu, h;\r
6738   int i = 0;\r
6739   hmenu = LoadMenu(hInst, "TextMenu");\r
6740   h = GetSubMenu(hmenu, 0);\r
6741   while (e->item) {\r
6742     if (strcmp(e->item, "-") == 0) {\r
6743       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6744     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6745       int flags = MF_STRING, j = 0;\r
6746       if (e->item[0] == '|') {\r
6747         flags |= MF_MENUBARBREAK;\r
6748         j++;\r
6749       }\r
6750       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6751       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6752     }\r
6753     e++;\r
6754     i++;\r
6755   } \r
6756   return hmenu;\r
6757 }\r
6758 \r
6759 WNDPROC consoleTextWindowProc;\r
6760 \r
6761 void\r
6762 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6763 {\r
6764   char buf[MSG_SIZ], name[MSG_SIZ];\r
6765   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6766   CHARRANGE sel;\r
6767 \r
6768   if (!getname) {\r
6769     SetWindowText(hInput, command);\r
6770     if (immediate) {\r
6771       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6772     } else {\r
6773       sel.cpMin = 999999;\r
6774       sel.cpMax = 999999;\r
6775       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6776       SetFocus(hInput);\r
6777     }\r
6778     return;\r
6779   }    \r
6780   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6781   if (sel.cpMin == sel.cpMax) {\r
6782     /* Expand to surrounding word */\r
6783     TEXTRANGE tr;\r
6784     do {\r
6785       tr.chrg.cpMax = sel.cpMin;\r
6786       tr.chrg.cpMin = --sel.cpMin;\r
6787       if (sel.cpMin < 0) break;\r
6788       tr.lpstrText = name;\r
6789       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6790     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6791     sel.cpMin++;\r
6792 \r
6793     do {\r
6794       tr.chrg.cpMin = sel.cpMax;\r
6795       tr.chrg.cpMax = ++sel.cpMax;\r
6796       tr.lpstrText = name;\r
6797       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6798     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6799     sel.cpMax--;\r
6800 \r
6801     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6802       MessageBeep(MB_ICONEXCLAMATION);\r
6803       return;\r
6804     }\r
6805     tr.chrg = sel;\r
6806     tr.lpstrText = name;\r
6807     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6808   } else {\r
6809     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6810       MessageBeep(MB_ICONEXCLAMATION);\r
6811       return;\r
6812     }\r
6813     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6814   }\r
6815   if (immediate) {\r
6816     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6817     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6818     SetWindowText(hInput, buf);\r
6819     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6820   } else {\r
6821     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6822       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6823     SetWindowText(hInput, buf);\r
6824     sel.cpMin = 999999;\r
6825     sel.cpMax = 999999;\r
6826     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6827     SetFocus(hInput);\r
6828   }\r
6829 }\r
6830 \r
6831 LRESULT CALLBACK \r
6832 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6833 {\r
6834   HWND hInput;\r
6835   CHARRANGE sel;\r
6836 \r
6837   switch (message) {\r
6838   case WM_KEYDOWN:\r
6839     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6840     if(wParam=='R') return 0;\r
6841     switch (wParam) {\r
6842     case VK_PRIOR:\r
6843       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6844       return 0;\r
6845     case VK_NEXT:\r
6846       sel.cpMin = 999999;\r
6847       sel.cpMax = 999999;\r
6848       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6849       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6850       return 0;\r
6851     }\r
6852     break;\r
6853   case WM_CHAR:\r
6854    if(wParam != '\022') {\r
6855     if (wParam == '\t') {\r
6856       if (GetKeyState(VK_SHIFT) < 0) {\r
6857         /* shifted */\r
6858         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6859         if (buttonDesc[0].hwnd) {\r
6860           SetFocus(buttonDesc[0].hwnd);\r
6861         } else {\r
6862           SetFocus(hwndMain);\r
6863         }\r
6864       } else {\r
6865         /* unshifted */\r
6866         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6867       }\r
6868     } else {\r
6869       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6870       JAWS_DELETE( SetFocus(hInput); )\r
6871       SendMessage(hInput, message, wParam, lParam);\r
6872     }\r
6873     return 0;\r
6874    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
6875    lParam = -1;\r
6876   case WM_RBUTTONDOWN:\r
6877     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6878       /* Move selection here if it was empty */\r
6879       POINT pt;\r
6880       pt.x = LOWORD(lParam);\r
6881       pt.y = HIWORD(lParam);\r
6882       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6883       if (sel.cpMin == sel.cpMax) {\r
6884         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6885         sel.cpMax = sel.cpMin;\r
6886         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6887       }\r
6888       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6889 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6890       POINT pt;\r
6891       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6892       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6893       if (sel.cpMin == sel.cpMax) {\r
6894         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6895         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6896       }\r
6897       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6898         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6899       }\r
6900       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6901       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6902       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6903       MenuPopup(hwnd, pt, hmenu, -1);\r
6904 }\r
6905     }\r
6906     return 0;\r
6907   case WM_RBUTTONUP:\r
6908     if (GetKeyState(VK_SHIFT) & ~1) {\r
6909       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6910         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6911     }\r
6912     return 0;\r
6913   case WM_PASTE:\r
6914     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6915     SetFocus(hInput);\r
6916     return SendMessage(hInput, message, wParam, lParam);\r
6917   case WM_MBUTTONDOWN:\r
6918     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6919   case WM_COMMAND:\r
6920     switch (LOWORD(wParam)) {\r
6921     case IDM_QuickPaste:\r
6922       {\r
6923         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6924         if (sel.cpMin == sel.cpMax) {\r
6925           MessageBeep(MB_ICONEXCLAMATION);\r
6926           return 0;\r
6927         }\r
6928         SendMessage(hwnd, WM_COPY, 0, 0);\r
6929         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6930         SendMessage(hInput, WM_PASTE, 0, 0);\r
6931         SetFocus(hInput);\r
6932         return 0;\r
6933       }\r
6934     case IDM_Cut:\r
6935       SendMessage(hwnd, WM_CUT, 0, 0);\r
6936       return 0;\r
6937     case IDM_Paste:\r
6938       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6939       return 0;\r
6940     case IDM_Copy:\r
6941       SendMessage(hwnd, WM_COPY, 0, 0);\r
6942       return 0;\r
6943     default:\r
6944       {\r
6945         int i = LOWORD(wParam) - IDM_CommandX;\r
6946         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6947             icsTextMenuEntry[i].command != NULL) {\r
6948           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6949                    icsTextMenuEntry[i].getname,\r
6950                    icsTextMenuEntry[i].immediate);\r
6951           return 0;\r
6952         }\r
6953       }\r
6954       break;\r
6955     }\r
6956     break;\r
6957   }\r
6958   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
6959 }\r
6960 \r
6961 WNDPROC consoleInputWindowProc;\r
6962 \r
6963 LRESULT CALLBACK\r
6964 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6965 {\r
6966   char buf[MSG_SIZ];\r
6967   char *p;\r
6968   static BOOL sendNextChar = FALSE;\r
6969   static BOOL quoteNextChar = FALSE;\r
6970   InputSource *is = consoleInputSource;\r
6971   CHARFORMAT cf;\r
6972   CHARRANGE sel;\r
6973 \r
6974   switch (message) {\r
6975   case WM_CHAR:\r
6976     if (!appData.localLineEditing || sendNextChar) {\r
6977       is->buf[0] = (CHAR) wParam;\r
6978       is->count = 1;\r
6979       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6980       sendNextChar = FALSE;\r
6981       return 0;\r
6982     }\r
6983     if (quoteNextChar) {\r
6984       buf[0] = (char) wParam;\r
6985       buf[1] = NULLCHAR;\r
6986       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
6987       quoteNextChar = FALSE;\r
6988       return 0;\r
6989     }\r
6990     switch (wParam) {\r
6991     case '\r':   /* Enter key */\r
6992       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
6993       if (consoleEcho) SaveInHistory(is->buf);\r
6994       is->buf[is->count++] = '\n';\r
6995       is->buf[is->count] = NULLCHAR;\r
6996       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6997       if (consoleEcho) {\r
6998         ConsoleOutput(is->buf, is->count, TRUE);\r
6999       } else if (appData.localLineEditing) {\r
7000         ConsoleOutput("\n", 1, TRUE);\r
7001       }\r
7002       /* fall thru */\r
7003     case '\033': /* Escape key */\r
7004       SetWindowText(hwnd, "");\r
7005       cf.cbSize = sizeof(CHARFORMAT);\r
7006       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7007       if (consoleEcho) {\r
7008         cf.crTextColor = textAttribs[ColorNormal].color;\r
7009       } else {\r
7010         cf.crTextColor = COLOR_ECHOOFF;\r
7011       }\r
7012       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7013       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7014       return 0;\r
7015     case '\t':   /* Tab key */\r
7016       if (GetKeyState(VK_SHIFT) < 0) {\r
7017         /* shifted */\r
7018         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7019       } else {\r
7020         /* unshifted */\r
7021         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7022         if (buttonDesc[0].hwnd) {\r
7023           SetFocus(buttonDesc[0].hwnd);\r
7024         } else {\r
7025           SetFocus(hwndMain);\r
7026         }\r
7027       }\r
7028       return 0;\r
7029     case '\023': /* Ctrl+S */\r
7030       sendNextChar = TRUE;\r
7031       return 0;\r
7032     case '\021': /* Ctrl+Q */\r
7033       quoteNextChar = TRUE;\r
7034       return 0;\r
7035     JAWS_REPLAY\r
7036     default:\r
7037       break;\r
7038     }\r
7039     break;\r
7040   case WM_KEYDOWN:\r
7041     switch (wParam) {\r
7042     case VK_UP:\r
7043       GetWindowText(hwnd, buf, MSG_SIZ);\r
7044       p = PrevInHistory(buf);\r
7045       if (p != NULL) {\r
7046         SetWindowText(hwnd, p);\r
7047         sel.cpMin = 999999;\r
7048         sel.cpMax = 999999;\r
7049         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7050         return 0;\r
7051       }\r
7052       break;\r
7053     case VK_DOWN:\r
7054       p = NextInHistory();\r
7055       if (p != NULL) {\r
7056         SetWindowText(hwnd, p);\r
7057         sel.cpMin = 999999;\r
7058         sel.cpMax = 999999;\r
7059         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7060         return 0;\r
7061       }\r
7062       break;\r
7063     case VK_HOME:\r
7064     case VK_END:\r
7065       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7066       /* fall thru */\r
7067     case VK_PRIOR:\r
7068     case VK_NEXT:\r
7069       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7070       return 0;\r
7071     }\r
7072     break;\r
7073   case WM_MBUTTONDOWN:\r
7074     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7075       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7076     break;\r
7077   case WM_RBUTTONUP:\r
7078     if (GetKeyState(VK_SHIFT) & ~1) {\r
7079       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7080         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7081     } else {\r
7082       POINT pt;\r
7083       HMENU hmenu;\r
7084       hmenu = LoadMenu(hInst, "InputMenu");\r
7085       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7086       if (sel.cpMin == sel.cpMax) {\r
7087         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7088         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7089       }\r
7090       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7091         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7092       }\r
7093       pt.x = LOWORD(lParam);\r
7094       pt.y = HIWORD(lParam);\r
7095       MenuPopup(hwnd, pt, hmenu, -1);\r
7096     }\r
7097     return 0;\r
7098   case WM_COMMAND:\r
7099     switch (LOWORD(wParam)) { \r
7100     case IDM_Undo:\r
7101       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7102       return 0;\r
7103     case IDM_SelectAll:\r
7104       sel.cpMin = 0;\r
7105       sel.cpMax = -1; /*999999?*/\r
7106       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7107       return 0;\r
7108     case IDM_Cut:\r
7109       SendMessage(hwnd, WM_CUT, 0, 0);\r
7110       return 0;\r
7111     case IDM_Paste:\r
7112       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7113       return 0;\r
7114     case IDM_Copy:\r
7115       SendMessage(hwnd, WM_COPY, 0, 0);\r
7116       return 0;\r
7117     }\r
7118     break;\r
7119   }\r
7120   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7121 }\r
7122 \r
7123 #define CO_MAX  100000\r
7124 #define CO_TRIM   1000\r
7125 \r
7126 LRESULT CALLBACK\r
7127 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7128 {\r
7129   static SnapData sd;\r
7130   HWND hText, hInput;\r
7131   RECT rect;\r
7132   static int sizeX, sizeY;\r
7133   int newSizeX, newSizeY;\r
7134   MINMAXINFO *mmi;\r
7135   WORD wMask;\r
7136 \r
7137   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7138   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7139 \r
7140   switch (message) {\r
7141   case WM_NOTIFY:\r
7142     if (((NMHDR*)lParam)->code == EN_LINK)\r
7143     {\r
7144       ENLINK *pLink = (ENLINK*)lParam;\r
7145       if (pLink->msg == WM_LBUTTONUP)\r
7146       {\r
7147         TEXTRANGE tr;\r
7148 \r
7149         tr.chrg = pLink->chrg;\r
7150         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7151         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7152         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7153         free(tr.lpstrText);\r
7154       }\r
7155     }\r
7156     break;\r
7157   case WM_INITDIALOG: /* message: initialize dialog box */\r
7158     hwndConsole = hDlg;\r
7159     SetFocus(hInput);\r
7160     consoleTextWindowProc = (WNDPROC)\r
7161       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7162     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7163     consoleInputWindowProc = (WNDPROC)\r
7164       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7165     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7166     Colorize(ColorNormal, TRUE);\r
7167     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7168     ChangedConsoleFont();\r
7169     GetClientRect(hDlg, &rect);\r
7170     sizeX = rect.right;\r
7171     sizeY = rect.bottom;\r
7172     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7173         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7174       WINDOWPLACEMENT wp;\r
7175       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7176       wp.length = sizeof(WINDOWPLACEMENT);\r
7177       wp.flags = 0;\r
7178       wp.showCmd = SW_SHOW;\r
7179       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7180       wp.rcNormalPosition.left = wpConsole.x;\r
7181       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7182       wp.rcNormalPosition.top = wpConsole.y;\r
7183       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7184       SetWindowPlacement(hDlg, &wp);\r
7185     }\r
7186 \r
7187    // [HGM] Chessknight's change 2004-07-13\r
7188    else { /* Determine Defaults */\r
7189        WINDOWPLACEMENT wp;\r
7190        wpConsole.x = wpMain.width + 1;\r
7191        wpConsole.y = wpMain.y;\r
7192        wpConsole.width = screenWidth -  wpMain.width;\r
7193        wpConsole.height = wpMain.height;\r
7194        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7195        wp.length = sizeof(WINDOWPLACEMENT);\r
7196        wp.flags = 0;\r
7197        wp.showCmd = SW_SHOW;\r
7198        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7199        wp.rcNormalPosition.left = wpConsole.x;\r
7200        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7201        wp.rcNormalPosition.top = wpConsole.y;\r
7202        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7203        SetWindowPlacement(hDlg, &wp);\r
7204     }\r
7205 \r
7206    // Allow hText to highlight URLs and send notifications on them\r
7207    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7208    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7209    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7210    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7211 \r
7212     return FALSE;\r
7213 \r
7214   case WM_SETFOCUS:\r
7215     SetFocus(hInput);\r
7216     return 0;\r
7217 \r
7218   case WM_CLOSE:\r
7219     ExitEvent(0);\r
7220     /* not reached */\r
7221     break;\r
7222 \r
7223   case WM_SIZE:\r
7224     if (IsIconic(hDlg)) break;\r
7225     newSizeX = LOWORD(lParam);\r
7226     newSizeY = HIWORD(lParam);\r
7227     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7228       RECT rectText, rectInput;\r
7229       POINT pt;\r
7230       int newTextHeight, newTextWidth;\r
7231       GetWindowRect(hText, &rectText);\r
7232       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7233       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7234       if (newTextHeight < 0) {\r
7235         newSizeY += -newTextHeight;\r
7236         newTextHeight = 0;\r
7237       }\r
7238       SetWindowPos(hText, NULL, 0, 0,\r
7239         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7240       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7241       pt.x = rectInput.left;\r
7242       pt.y = rectInput.top + newSizeY - sizeY;\r
7243       ScreenToClient(hDlg, &pt);\r
7244       SetWindowPos(hInput, NULL, \r
7245         pt.x, pt.y, /* needs client coords */   \r
7246         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7247         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7248     }\r
7249     sizeX = newSizeX;\r
7250     sizeY = newSizeY;\r
7251     break;\r
7252 \r
7253   case WM_GETMINMAXINFO:\r
7254     /* Prevent resizing window too small */\r
7255     mmi = (MINMAXINFO *) lParam;\r
7256     mmi->ptMinTrackSize.x = 100;\r
7257     mmi->ptMinTrackSize.y = 100;\r
7258     break;\r
7259 \r
7260   /* [AS] Snapping */\r
7261   case WM_ENTERSIZEMOVE:\r
7262     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7263 \r
7264   case WM_SIZING:\r
7265     return OnSizing( &sd, hDlg, wParam, lParam );\r
7266 \r
7267   case WM_MOVING:\r
7268     return OnMoving( &sd, hDlg, wParam, lParam );\r
7269 \r
7270   case WM_EXITSIZEMOVE:\r
7271         UpdateICSWidth(hText);\r
7272     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7273   }\r
7274 \r
7275   return DefWindowProc(hDlg, message, wParam, lParam);\r
7276 }\r
7277 \r
7278 \r
7279 VOID\r
7280 ConsoleCreate()\r
7281 {\r
7282   HWND hCons;\r
7283   if (hwndConsole) return;\r
7284   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7285   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7286 }\r
7287 \r
7288 \r
7289 VOID\r
7290 ConsoleOutput(char* data, int length, int forceVisible)\r
7291 {\r
7292   HWND hText;\r
7293   int trim, exlen;\r
7294   char *p, *q;\r
7295   char buf[CO_MAX+1];\r
7296   POINT pEnd;\r
7297   RECT rect;\r
7298   static int delayLF = 0;\r
7299   CHARRANGE savesel, sel;\r
7300 \r
7301   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7302   p = data;\r
7303   q = buf;\r
7304   if (delayLF) {\r
7305     *q++ = '\r';\r
7306     *q++ = '\n';\r
7307     delayLF = 0;\r
7308   }\r
7309   while (length--) {\r
7310     if (*p == '\n') {\r
7311       if (*++p) {\r
7312         *q++ = '\r';\r
7313         *q++ = '\n';\r
7314       } else {\r
7315         delayLF = 1;\r
7316       }\r
7317     } else if (*p == '\007') {\r
7318        MyPlaySound(&sounds[(int)SoundBell]);\r
7319        p++;\r
7320     } else {\r
7321       *q++ = *p++;\r
7322     }\r
7323   }\r
7324   *q = NULLCHAR;\r
7325   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7326   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7327   /* Save current selection */\r
7328   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7329   exlen = GetWindowTextLength(hText);\r
7330   /* Find out whether current end of text is visible */\r
7331   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7332   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7333   /* Trim existing text if it's too long */\r
7334   if (exlen + (q - buf) > CO_MAX) {\r
7335     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7336     sel.cpMin = 0;\r
7337     sel.cpMax = trim;\r
7338     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7339     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7340     exlen -= trim;\r
7341     savesel.cpMin -= trim;\r
7342     savesel.cpMax -= trim;\r
7343     if (exlen < 0) exlen = 0;\r
7344     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7345     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7346   }\r
7347   /* Append the new text */\r
7348   sel.cpMin = exlen;\r
7349   sel.cpMax = exlen;\r
7350   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7351   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7352   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7353   if (forceVisible || exlen == 0 ||\r
7354       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7355        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7356     /* Scroll to make new end of text visible if old end of text\r
7357        was visible or new text is an echo of user typein */\r
7358     sel.cpMin = 9999999;\r
7359     sel.cpMax = 9999999;\r
7360     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7361     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7362     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7363     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7364   }\r
7365   if (savesel.cpMax == exlen || forceVisible) {\r
7366     /* Move insert point to new end of text if it was at the old\r
7367        end of text or if the new text is an echo of user typein */\r
7368     sel.cpMin = 9999999;\r
7369     sel.cpMax = 9999999;\r
7370     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7371   } else {\r
7372     /* Restore previous selection */\r
7373     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7374   }\r
7375   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7376 }\r
7377 \r
7378 /*---------*/\r
7379 \r
7380 \r
7381 void\r
7382 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7383 {\r
7384   char buf[100];\r
7385   char *str;\r
7386   COLORREF oldFg, oldBg;\r
7387   HFONT oldFont;\r
7388   RECT rect;\r
7389 \r
7390   if(copyNumber > 1)
7391     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7392 \r
7393   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7394   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7395   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7396 \r
7397   rect.left = x;\r
7398   rect.right = x + squareSize;\r
7399   rect.top  = y;\r
7400   rect.bottom = y + squareSize;\r
7401   str = buf;\r
7402 \r
7403   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7404                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7405              y, ETO_CLIPPED|ETO_OPAQUE,\r
7406              &rect, str, strlen(str), NULL);\r
7407 \r
7408   (void) SetTextColor(hdc, oldFg);\r
7409   (void) SetBkColor(hdc, oldBg);\r
7410   (void) SelectObject(hdc, oldFont);\r
7411 }\r
7412 \r
7413 void\r
7414 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7415               RECT *rect, char *color, char *flagFell)\r
7416 {\r
7417   char buf[100];\r
7418   char *str;\r
7419   COLORREF oldFg, oldBg;\r
7420   HFONT oldFont;\r
7421 \r
7422   if (appData.clockMode) {\r
7423     if (tinyLayout)\r
7424       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7425     else\r
7426       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7427     str = buf;\r
7428   } else {\r
7429     str = color;\r
7430   }\r
7431 \r
7432   if (highlight) {\r
7433     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7434     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7435   } else {\r
7436     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7437     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7438   }\r
7439   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7440 \r
7441   JAWS_SILENCE\r
7442 \r
7443   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7444              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7445              rect, str, strlen(str), NULL);\r
7446   if(logoHeight > 0 && appData.clockMode) {\r
7447       RECT r;\r
7448       str += strlen(color)+2;\r
7449       r.top = rect->top + logoHeight/2;\r
7450       r.left = rect->left;\r
7451       r.right = rect->right;\r
7452       r.bottom = rect->bottom;\r
7453       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7454                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7455                  &r, str, strlen(str), NULL);\r
7456   }\r
7457   (void) SetTextColor(hdc, oldFg);\r
7458   (void) SetBkColor(hdc, oldBg);\r
7459   (void) SelectObject(hdc, oldFont);\r
7460 }\r
7461 \r
7462 \r
7463 int\r
7464 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7465            OVERLAPPED *ovl)\r
7466 {\r
7467   int ok, err;\r
7468 \r
7469   /* [AS]  */\r
7470   if( count <= 0 ) {\r
7471     if (appData.debugMode) {\r
7472       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7473     }\r
7474 \r
7475     return ERROR_INVALID_USER_BUFFER;\r
7476   }\r
7477 \r
7478   ResetEvent(ovl->hEvent);\r
7479   ovl->Offset = ovl->OffsetHigh = 0;\r
7480   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7481   if (ok) {\r
7482     err = NO_ERROR;\r
7483   } else {\r
7484     err = GetLastError();\r
7485     if (err == ERROR_IO_PENDING) {\r
7486       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7487       if (ok)\r
7488         err = NO_ERROR;\r
7489       else\r
7490         err = GetLastError();\r
7491     }\r
7492   }\r
7493   return err;\r
7494 }\r
7495 \r
7496 int\r
7497 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7498             OVERLAPPED *ovl)\r
7499 {\r
7500   int ok, err;\r
7501 \r
7502   ResetEvent(ovl->hEvent);\r
7503   ovl->Offset = ovl->OffsetHigh = 0;\r
7504   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7505   if (ok) {\r
7506     err = NO_ERROR;\r
7507   } else {\r
7508     err = GetLastError();\r
7509     if (err == ERROR_IO_PENDING) {\r
7510       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7511       if (ok)\r
7512         err = NO_ERROR;\r
7513       else\r
7514         err = GetLastError();\r
7515     }\r
7516   }\r
7517   return err;\r
7518 }\r
7519 \r
7520 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7521 void CheckForInputBufferFull( InputSource * is )\r
7522 {\r
7523     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7524         /* Look for end of line */\r
7525         char * p = is->buf;\r
7526         \r
7527         while( p < is->next && *p != '\n' ) {\r
7528             p++;\r
7529         }\r
7530 \r
7531         if( p >= is->next ) {\r
7532             if (appData.debugMode) {\r
7533                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7534             }\r
7535 \r
7536             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7537             is->count = (DWORD) -1;\r
7538             is->next = is->buf;\r
7539         }\r
7540     }\r
7541 }\r
7542 \r
7543 DWORD\r
7544 InputThread(LPVOID arg)\r
7545 {\r
7546   InputSource *is;\r
7547   OVERLAPPED ovl;\r
7548 \r
7549   is = (InputSource *) arg;\r
7550   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7551   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7552   while (is->hThread != NULL) {\r
7553     is->error = DoReadFile(is->hFile, is->next,\r
7554                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7555                            &is->count, &ovl);\r
7556     if (is->error == NO_ERROR) {\r
7557       is->next += is->count;\r
7558     } else {\r
7559       if (is->error == ERROR_BROKEN_PIPE) {\r
7560         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7561         is->count = 0;\r
7562       } else {\r
7563         is->count = (DWORD) -1;\r
7564         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7565         break; \r
7566       }\r
7567     }\r
7568 \r
7569     CheckForInputBufferFull( is );\r
7570 \r
7571     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7572 \r
7573     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7574 \r
7575     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7576   }\r
7577 \r
7578   CloseHandle(ovl.hEvent);\r
7579   CloseHandle(is->hFile);\r
7580 \r
7581   if (appData.debugMode) {\r
7582     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7583   }\r
7584 \r
7585   return 0;\r
7586 }\r
7587 \r
7588 \r
7589 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7590 DWORD\r
7591 NonOvlInputThread(LPVOID arg)\r
7592 {\r
7593   InputSource *is;\r
7594   char *p, *q;\r
7595   int i;\r
7596   char prev;\r
7597 \r
7598   is = (InputSource *) arg;\r
7599   while (is->hThread != NULL) {\r
7600     is->error = ReadFile(is->hFile, is->next,\r
7601                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7602                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7603     if (is->error == NO_ERROR) {\r
7604       /* Change CRLF to LF */\r
7605       if (is->next > is->buf) {\r
7606         p = is->next - 1;\r
7607         i = is->count + 1;\r
7608       } else {\r
7609         p = is->next;\r
7610         i = is->count;\r
7611       }\r
7612       q = p;\r
7613       prev = NULLCHAR;\r
7614       while (i > 0) {\r
7615         if (prev == '\r' && *p == '\n') {\r
7616           *(q-1) = '\n';\r
7617           is->count--;\r
7618         } else { \r
7619           *q++ = *p;\r
7620         }\r
7621         prev = *p++;\r
7622         i--;\r
7623       }\r
7624       *q = NULLCHAR;\r
7625       is->next = q;\r
7626     } else {\r
7627       if (is->error == ERROR_BROKEN_PIPE) {\r
7628         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7629         is->count = 0; \r
7630       } else {\r
7631         is->count = (DWORD) -1;\r
7632       }\r
7633     }\r
7634 \r
7635     CheckForInputBufferFull( is );\r
7636 \r
7637     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7638 \r
7639     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7640 \r
7641     if (is->count < 0) break;  /* Quit on error */\r
7642   }\r
7643   CloseHandle(is->hFile);\r
7644   return 0;\r
7645 }\r
7646 \r
7647 DWORD\r
7648 SocketInputThread(LPVOID arg)\r
7649 {\r
7650   InputSource *is;\r
7651 \r
7652   is = (InputSource *) arg;\r
7653   while (is->hThread != NULL) {\r
7654     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7655     if ((int)is->count == SOCKET_ERROR) {\r
7656       is->count = (DWORD) -1;\r
7657       is->error = WSAGetLastError();\r
7658     } else {\r
7659       is->error = NO_ERROR;\r
7660       is->next += is->count;\r
7661       if (is->count == 0 && is->second == is) {\r
7662         /* End of file on stderr; quit with no message */\r
7663         break;\r
7664       }\r
7665     }\r
7666     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7667 \r
7668     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7669 \r
7670     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7671   }\r
7672   return 0;\r
7673 }\r
7674 \r
7675 VOID\r
7676 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7677 {\r
7678   InputSource *is;\r
7679 \r
7680   is = (InputSource *) lParam;\r
7681   if (is->lineByLine) {\r
7682     /* Feed in lines one by one */\r
7683     char *p = is->buf;\r
7684     char *q = p;\r
7685     while (q < is->next) {\r
7686       if (*q++ == '\n') {\r
7687         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7688         p = q;\r
7689       }\r
7690     }\r
7691     \r
7692     /* Move any partial line to the start of the buffer */\r
7693     q = is->buf;\r
7694     while (p < is->next) {\r
7695       *q++ = *p++;\r
7696     }\r
7697     is->next = q;\r
7698 \r
7699     if (is->error != NO_ERROR || is->count == 0) {\r
7700       /* Notify backend of the error.  Note: If there was a partial\r
7701          line at the end, it is not flushed through. */\r
7702       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7703     }\r
7704   } else {\r
7705     /* Feed in the whole chunk of input at once */\r
7706     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7707     is->next = is->buf;\r
7708   }\r
7709 }\r
7710 \r
7711 /*---------------------------------------------------------------------------*\\r
7712  *\r
7713  *  Menu enables. Used when setting various modes.\r
7714  *\r
7715 \*---------------------------------------------------------------------------*/\r
7716 \r
7717 typedef struct {\r
7718   int item;\r
7719   int flags;\r
7720 } Enables;\r
7721 \r
7722 VOID\r
7723 GreyRevert(Boolean grey)\r
7724 { // [HGM] vari: for retracting variations in local mode\r
7725   HMENU hmenu = GetMenu(hwndMain);\r
7726   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7727   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7728 }\r
7729 \r
7730 VOID\r
7731 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7732 {\r
7733   while (enab->item > 0) {\r
7734     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7735     enab++;\r
7736   }\r
7737 }\r
7738 \r
7739 Enables gnuEnables[] = {\r
7740   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7741   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7742   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7743   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7744   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7745   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7746   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7747   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7748   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7749   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7750   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7751   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7752   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7753   { -1, -1 }\r
7754 };\r
7755 \r
7756 Enables icsEnables[] = {\r
7757   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7758   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7759   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7760   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7761   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7762   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7763   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7764   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7765   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7766   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7767   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7768   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7769   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7770   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7771   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7772   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7773   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7774   { -1, -1 }\r
7775 };\r
7776 \r
7777 #if ZIPPY\r
7778 Enables zippyEnables[] = {\r
7779   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7780   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7781   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7782   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7783   { -1, -1 }\r
7784 };\r
7785 #endif\r
7786 \r
7787 Enables ncpEnables[] = {\r
7788   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7789   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7790   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7791   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7792   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7793   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7794   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7795   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7796   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7797   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7798   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7799   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7800   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7801   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7802   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7803   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7804   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7805   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7806   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7807   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7808   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7809   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7810   { -1, -1 }\r
7811 };\r
7812 \r
7813 Enables trainingOnEnables[] = {\r
7814   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7815   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7816   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7817   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7818   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7819   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7820   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7821   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7822   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7823   { -1, -1 }\r
7824 };\r
7825 \r
7826 Enables trainingOffEnables[] = {\r
7827   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7828   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7829   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7830   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7831   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7832   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7833   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7834   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7835   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7836   { -1, -1 }\r
7837 };\r
7838 \r
7839 /* These modify either ncpEnables or gnuEnables */\r
7840 Enables cmailEnables[] = {\r
7841   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7842   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7843   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7844   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7845   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7846   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7847   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7848   { -1, -1 }\r
7849 };\r
7850 \r
7851 Enables machineThinkingEnables[] = {\r
7852   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7853   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7854   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7855   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7856   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7857   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7858   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7859   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7860   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7861   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7862   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7863   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7864   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7865 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7866   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7867   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7868   { -1, -1 }\r
7869 };\r
7870 \r
7871 Enables userThinkingEnables[] = {\r
7872   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7873   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7874   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7875   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7876   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7877   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7878   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7879   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7880   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7881   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7882   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7883   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7884   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7885 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7886   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7887   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7888   { -1, -1 }\r
7889 };\r
7890 \r
7891 /*---------------------------------------------------------------------------*\\r
7892  *\r
7893  *  Front-end interface functions exported by XBoard.\r
7894  *  Functions appear in same order as prototypes in frontend.h.\r
7895  * \r
7896 \*---------------------------------------------------------------------------*/\r
7897 VOID\r
7898 CheckMark(UINT item, int state)\r
7899 {\r
7900     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
7901 }\r
7902 \r
7903 VOID\r
7904 ModeHighlight()\r
7905 {\r
7906   static UINT prevChecked = 0;\r
7907   static int prevPausing = 0;\r
7908   UINT nowChecked;\r
7909 \r
7910   if (pausing != prevPausing) {\r
7911     prevPausing = pausing;\r
7912     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7913                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7914     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7915   }\r
7916 \r
7917   switch (gameMode) {\r
7918   case BeginningOfGame:\r
7919     if (appData.icsActive)\r
7920       nowChecked = IDM_IcsClient;\r
7921     else if (appData.noChessProgram)\r
7922       nowChecked = IDM_EditGame;\r
7923     else\r
7924       nowChecked = IDM_MachineBlack;\r
7925     break;\r
7926   case MachinePlaysBlack:\r
7927     nowChecked = IDM_MachineBlack;\r
7928     break;\r
7929   case MachinePlaysWhite:\r
7930     nowChecked = IDM_MachineWhite;\r
7931     break;\r
7932   case TwoMachinesPlay:\r
7933     nowChecked = IDM_TwoMachines;\r
7934     break;\r
7935   case AnalyzeMode:\r
7936     nowChecked = IDM_AnalysisMode;\r
7937     break;\r
7938   case AnalyzeFile:\r
7939     nowChecked = IDM_AnalyzeFile;\r
7940     break;\r
7941   case EditGame:\r
7942     nowChecked = IDM_EditGame;\r
7943     break;\r
7944   case PlayFromGameFile:\r
7945     nowChecked = IDM_LoadGame;\r
7946     break;\r
7947   case EditPosition:\r
7948     nowChecked = IDM_EditPosition;\r
7949     break;\r
7950   case Training:\r
7951     nowChecked = IDM_Training;\r
7952     break;\r
7953   case IcsPlayingWhite:\r
7954   case IcsPlayingBlack:\r
7955   case IcsObserving:\r
7956   case IcsIdle:\r
7957     nowChecked = IDM_IcsClient;\r
7958     break;\r
7959   default:\r
7960   case EndOfGame:\r
7961     nowChecked = 0;\r
7962     break;\r
7963   }\r
7964   CheckMark(prevChecked, MF_UNCHECKED);\r
7965   CheckMark(nowChecked, MF_CHECKED);\r
7966   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
7967 \r
7968   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
7969     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
7970                           MF_BYCOMMAND|MF_ENABLED);\r
7971   } else {\r
7972     (void) EnableMenuItem(GetMenu(hwndMain), \r
7973                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
7974   }\r
7975 \r
7976   prevChecked = nowChecked;\r
7977 \r
7978   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
7979   if (appData.icsActive) {\r
7980        if (appData.icsEngineAnalyze) {\r
7981                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
7982        } else {\r
7983                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
7984        }\r
7985   }\r
7986   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
7987 }\r
7988 \r
7989 VOID\r
7990 SetICSMode()\r
7991 {\r
7992   HMENU hmenu = GetMenu(hwndMain);\r
7993   SetMenuEnables(hmenu, icsEnables);\r
7994   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
7995     MF_BYCOMMAND|MF_ENABLED);\r
7996 #if ZIPPY\r
7997   if (appData.zippyPlay) {\r
7998     SetMenuEnables(hmenu, zippyEnables);\r
7999     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8000          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8001           MF_BYCOMMAND|MF_ENABLED);\r
8002   }\r
8003 #endif\r
8004 }\r
8005 \r
8006 VOID\r
8007 SetGNUMode()\r
8008 {\r
8009   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8010 }\r
8011 \r
8012 VOID\r
8013 SetNCPMode()\r
8014 {\r
8015   HMENU hmenu = GetMenu(hwndMain);\r
8016   SetMenuEnables(hmenu, ncpEnables);\r
8017     DrawMenuBar(hwndMain);\r
8018 }\r
8019 \r
8020 VOID\r
8021 SetCmailMode()\r
8022 {\r
8023   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8024 }\r
8025 \r
8026 VOID \r
8027 SetTrainingModeOn()\r
8028 {\r
8029   int i;\r
8030   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8031   for (i = 0; i < N_BUTTONS; i++) {\r
8032     if (buttonDesc[i].hwnd != NULL)\r
8033       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8034   }\r
8035   CommentPopDown();\r
8036 }\r
8037 \r
8038 VOID SetTrainingModeOff()\r
8039 {\r
8040   int i;\r
8041   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8042   for (i = 0; i < N_BUTTONS; i++) {\r
8043     if (buttonDesc[i].hwnd != NULL)\r
8044       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8045   }\r
8046 }\r
8047 \r
8048 \r
8049 VOID\r
8050 SetUserThinkingEnables()\r
8051 {\r
8052   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8053 }\r
8054 \r
8055 VOID\r
8056 SetMachineThinkingEnables()\r
8057 {\r
8058   HMENU hMenu = GetMenu(hwndMain);\r
8059   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8060 \r
8061   SetMenuEnables(hMenu, machineThinkingEnables);\r
8062 \r
8063   if (gameMode == MachinePlaysBlack) {\r
8064     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8065   } else if (gameMode == MachinePlaysWhite) {\r
8066     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8067   } else if (gameMode == TwoMachinesPlay) {\r
8068     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8069   }\r
8070 }\r
8071 \r
8072 \r
8073 VOID\r
8074 DisplayTitle(char *str)\r
8075 {\r
8076   char title[MSG_SIZ], *host;\r
8077   if (str[0] != NULLCHAR) {\r
8078     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8079   } else if (appData.icsActive) {\r
8080     if (appData.icsCommPort[0] != NULLCHAR)\r
8081       host = "ICS";\r
8082     else \r
8083       host = appData.icsHost;\r
8084       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8085   } else if (appData.noChessProgram) {\r
8086     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8087   } else {\r
8088     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8089     strcat(title, ": ");\r
8090     strcat(title, first.tidy);\r
8091   }\r
8092   SetWindowText(hwndMain, title);\r
8093 }\r
8094 \r
8095 \r
8096 VOID\r
8097 DisplayMessage(char *str1, char *str2)\r
8098 {\r
8099   HDC hdc;\r
8100   HFONT oldFont;\r
8101   int remain = MESSAGE_TEXT_MAX - 1;\r
8102   int len;\r
8103 \r
8104   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8105   messageText[0] = NULLCHAR;\r
8106   if (*str1) {\r
8107     len = strlen(str1);\r
8108     if (len > remain) len = remain;\r
8109     strncpy(messageText, str1, len);\r
8110     messageText[len] = NULLCHAR;\r
8111     remain -= len;\r
8112   }\r
8113   if (*str2 && remain >= 2) {\r
8114     if (*str1) {\r
8115       strcat(messageText, "  ");\r
8116       remain -= 2;\r
8117     }\r
8118     len = strlen(str2);\r
8119     if (len > remain) len = remain;\r
8120     strncat(messageText, str2, len);\r
8121   }\r
8122   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
8123   safeStrCpy(lastMsg, messageText, MSG_SIZ);
8124 \r
8125   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8126 \r
8127   SAYMACHINEMOVE();\r
8128 \r
8129   hdc = GetDC(hwndMain);\r
8130   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8131   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8132              &messageRect, messageText, strlen(messageText), NULL);\r
8133   (void) SelectObject(hdc, oldFont);\r
8134   (void) ReleaseDC(hwndMain, hdc);\r
8135 }\r
8136 \r
8137 VOID\r
8138 DisplayError(char *str, int error)\r
8139 {\r
8140   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8141   int len;\r
8142 \r
8143   if (error == 0) {\r
8144     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8145   } else {\r
8146     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8147                         NULL, error, LANG_NEUTRAL,\r
8148                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8149     if (len > 0) {\r
8150       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8151     } else {\r
8152       ErrorMap *em = errmap;\r
8153       while (em->err != 0 && em->err != error) em++;\r
8154       if (em->err != 0) {\r
8155         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8156       } else {\r
8157         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8158       }\r
8159     }\r
8160   }\r
8161   \r
8162   ErrorPopUp(_("Error"), buf);\r
8163 }\r
8164 \r
8165 \r
8166 VOID\r
8167 DisplayMoveError(char *str)\r
8168 {\r
8169   fromX = fromY = -1;\r
8170   ClearHighlights();\r
8171   DrawPosition(FALSE, NULL);\r
8172   if (appData.popupMoveErrors) {\r
8173     ErrorPopUp(_("Error"), str);\r
8174   } else {\r
8175     DisplayMessage(str, "");\r
8176     moveErrorMessageUp = TRUE;\r
8177   }\r
8178 }\r
8179 \r
8180 VOID\r
8181 DisplayFatalError(char *str, int error, int exitStatus)\r
8182 {\r
8183   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8184   int len;\r
8185   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8186 \r
8187   if (error != 0) {\r
8188     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8189                         NULL, error, LANG_NEUTRAL,\r
8190                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8191     if (len > 0) {\r
8192       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8193     } else {\r
8194       ErrorMap *em = errmap;\r
8195       while (em->err != 0 && em->err != error) em++;\r
8196       if (em->err != 0) {\r
8197         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8198       } else {\r
8199         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8200       }\r
8201     }\r
8202     str = buf;\r
8203   }\r
8204   if (appData.debugMode) {\r
8205     fprintf(debugFP, "%s: %s\n", label, str);\r
8206   }\r
8207   if (appData.popupExitMessage) {\r
8208     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8209                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8210   }\r
8211   ExitEvent(exitStatus);\r
8212 }\r
8213 \r
8214 \r
8215 VOID\r
8216 DisplayInformation(char *str)\r
8217 {\r
8218   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8219 }\r
8220 \r
8221 \r
8222 VOID\r
8223 DisplayNote(char *str)\r
8224 {\r
8225   ErrorPopUp(_("Note"), str);\r
8226 }\r
8227 \r
8228 \r
8229 typedef struct {\r
8230   char *title, *question, *replyPrefix;\r
8231   ProcRef pr;\r
8232 } QuestionParams;\r
8233 \r
8234 LRESULT CALLBACK\r
8235 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8236 {\r
8237   static QuestionParams *qp;\r
8238   char reply[MSG_SIZ];\r
8239   int len, err;\r
8240 \r
8241   switch (message) {\r
8242   case WM_INITDIALOG:\r
8243     qp = (QuestionParams *) lParam;\r
8244     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8245     Translate(hDlg, DLG_Question);\r
8246     SetWindowText(hDlg, qp->title);\r
8247     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8248     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8249     return FALSE;\r
8250 \r
8251   case WM_COMMAND:\r
8252     switch (LOWORD(wParam)) {\r
8253     case IDOK:\r
8254       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8255       if (*reply) strcat(reply, " ");\r
8256       len = strlen(reply);\r
8257       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8258       strcat(reply, "\n");\r
8259       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8260       EndDialog(hDlg, TRUE);\r
8261       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8262       return TRUE;\r
8263     case IDCANCEL:\r
8264       EndDialog(hDlg, FALSE);\r
8265       return TRUE;\r
8266     default:\r
8267       break;\r
8268     }\r
8269     break;\r
8270   }\r
8271   return FALSE;\r
8272 }\r
8273 \r
8274 VOID\r
8275 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8276 {\r
8277     QuestionParams qp;\r
8278     FARPROC lpProc;\r
8279     \r
8280     qp.title = title;\r
8281     qp.question = question;\r
8282     qp.replyPrefix = replyPrefix;\r
8283     qp.pr = pr;\r
8284     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8285     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8286       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8287     FreeProcInstance(lpProc);\r
8288 }\r
8289 \r
8290 /* [AS] Pick FRC position */\r
8291 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8292 {\r
8293     static int * lpIndexFRC;\r
8294     BOOL index_is_ok;\r
8295     char buf[16];\r
8296 \r
8297     switch( message )\r
8298     {\r
8299     case WM_INITDIALOG:\r
8300         lpIndexFRC = (int *) lParam;\r
8301 \r
8302         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8303         Translate(hDlg, DLG_NewGameFRC);\r
8304 \r
8305         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8306         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8307         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8308         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8309 \r
8310         break;\r
8311 \r
8312     case WM_COMMAND:\r
8313         switch( LOWORD(wParam) ) {\r
8314         case IDOK:\r
8315             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8316             EndDialog( hDlg, 0 );\r
8317             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8318             return TRUE;\r
8319         case IDCANCEL:\r
8320             EndDialog( hDlg, 1 );   \r
8321             return TRUE;\r
8322         case IDC_NFG_Edit:\r
8323             if( HIWORD(wParam) == EN_CHANGE ) {\r
8324                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8325 \r
8326                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8327             }\r
8328             return TRUE;\r
8329         case IDC_NFG_Random:\r
8330           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8331             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8332             return TRUE;\r
8333         }\r
8334 \r
8335         break;\r
8336     }\r
8337 \r
8338     return FALSE;\r
8339 }\r
8340 \r
8341 int NewGameFRC()\r
8342 {\r
8343     int result;\r
8344     int index = appData.defaultFrcPosition;\r
8345     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8346 \r
8347     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8348 \r
8349     if( result == 0 ) {\r
8350         appData.defaultFrcPosition = index;\r
8351     }\r
8352 \r
8353     return result;\r
8354 }\r
8355 \r
8356 /* [AS] Game list options. Refactored by HGM */\r
8357 \r
8358 HWND gameListOptionsDialog;\r
8359 \r
8360 // low-level front-end: clear text edit / list widget\r
8361 void\r
8362 GLT_ClearList()\r
8363 {\r
8364     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8365 }\r
8366 \r
8367 // low-level front-end: clear text edit / list widget\r
8368 void\r
8369 GLT_DeSelectList()\r
8370 {\r
8371     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8372 }\r
8373 \r
8374 // low-level front-end: append line to text edit / list widget\r
8375 void\r
8376 GLT_AddToList( char *name )\r
8377 {\r
8378     if( name != 0 ) {\r
8379             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8380     }\r
8381 }\r
8382 \r
8383 // low-level front-end: get line from text edit / list widget\r
8384 Boolean\r
8385 GLT_GetFromList( int index, char *name )\r
8386 {\r
8387     if( name != 0 ) {\r
8388             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8389                 return TRUE;\r
8390     }\r
8391     return FALSE;\r
8392 }\r
8393 \r
8394 void GLT_MoveSelection( HWND hDlg, int delta )\r
8395 {\r
8396     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8397     int idx2 = idx1 + delta;\r
8398     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8399 \r
8400     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8401         char buf[128];\r
8402 \r
8403         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8404         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8405         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8406         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8407     }\r
8408 }\r
8409 \r
8410 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8411 {\r
8412     switch( message )\r
8413     {\r
8414     case WM_INITDIALOG:\r
8415         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8416         \r
8417         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8418         Translate(hDlg, DLG_GameListOptions);\r
8419 \r
8420         /* Initialize list */\r
8421         GLT_TagsToList( lpUserGLT );\r
8422 \r
8423         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8424 \r
8425         break;\r
8426 \r
8427     case WM_COMMAND:\r
8428         switch( LOWORD(wParam) ) {\r
8429         case IDOK:\r
8430             GLT_ParseList();\r
8431             EndDialog( hDlg, 0 );\r
8432             return TRUE;\r
8433         case IDCANCEL:\r
8434             EndDialog( hDlg, 1 );\r
8435             return TRUE;\r
8436 \r
8437         case IDC_GLT_Default:\r
8438             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8439             return TRUE;\r
8440 \r
8441         case IDC_GLT_Restore:\r
8442             GLT_TagsToList( appData.gameListTags );\r
8443             return TRUE;\r
8444 \r
8445         case IDC_GLT_Up:\r
8446             GLT_MoveSelection( hDlg, -1 );\r
8447             return TRUE;\r
8448 \r
8449         case IDC_GLT_Down:\r
8450             GLT_MoveSelection( hDlg, +1 );\r
8451             return TRUE;\r
8452         }\r
8453 \r
8454         break;\r
8455     }\r
8456 \r
8457     return FALSE;\r
8458 }\r
8459 \r
8460 int GameListOptions()\r
8461 {\r
8462     int result;\r
8463     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8464 \r
8465       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8466 \r
8467     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8468 \r
8469     if( result == 0 ) {\r
8470         /* [AS] Memory leak here! */\r
8471         appData.gameListTags = strdup( lpUserGLT ); \r
8472     }\r
8473 \r
8474     return result;\r
8475 }\r
8476 \r
8477 VOID\r
8478 DisplayIcsInteractionTitle(char *str)\r
8479 {\r
8480   char consoleTitle[MSG_SIZ];\r
8481 \r
8482     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8483   SetWindowText(hwndConsole, consoleTitle);\r
8484 }\r
8485 \r
8486 void\r
8487 DrawPosition(int fullRedraw, Board board)\r
8488 {\r
8489   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8490 }\r
8491 \r
8492 void NotifyFrontendLogin()\r
8493 {\r
8494         if (hwndConsole)\r
8495                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8496 }\r
8497 \r
8498 VOID\r
8499 ResetFrontEnd()\r
8500 {\r
8501   fromX = fromY = -1;\r
8502   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8503     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8504     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8505     dragInfo.lastpos = dragInfo.pos;\r
8506     dragInfo.start.x = dragInfo.start.y = -1;\r
8507     dragInfo.from = dragInfo.start;\r
8508     ReleaseCapture();\r
8509     DrawPosition(TRUE, NULL);\r
8510   }\r
8511   TagsPopDown();\r
8512 }\r
8513 \r
8514 \r
8515 VOID\r
8516 CommentPopUp(char *title, char *str)\r
8517 {\r
8518   HWND hwnd = GetActiveWindow();\r
8519   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8520   SAY(str);\r
8521   SetActiveWindow(hwnd);\r
8522 }\r
8523 \r
8524 VOID\r
8525 CommentPopDown(void)\r
8526 {\r
8527   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8528   if (commentDialog) {\r
8529     ShowWindow(commentDialog, SW_HIDE);\r
8530   }\r
8531   commentUp = FALSE;\r
8532 }\r
8533 \r
8534 VOID\r
8535 EditCommentPopUp(int index, char *title, char *str)\r
8536 {\r
8537   EitherCommentPopUp(index, title, str, TRUE);\r
8538 }\r
8539 \r
8540 \r
8541 VOID\r
8542 RingBell()\r
8543 {\r
8544   MyPlaySound(&sounds[(int)SoundMove]);\r
8545 }\r
8546 \r
8547 VOID PlayIcsWinSound()\r
8548 {\r
8549   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8550 }\r
8551 \r
8552 VOID PlayIcsLossSound()\r
8553 {\r
8554   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8555 }\r
8556 \r
8557 VOID PlayIcsDrawSound()\r
8558 {\r
8559   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8560 }\r
8561 \r
8562 VOID PlayIcsUnfinishedSound()\r
8563 {\r
8564   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8565 }\r
8566 \r
8567 VOID\r
8568 PlayAlarmSound()\r
8569 {\r
8570   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8571 }\r
8572 \r
8573 \r
8574 VOID\r
8575 EchoOn()\r
8576 {\r
8577   HWND hInput;\r
8578   consoleEcho = TRUE;\r
8579   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8580   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8581   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8582 }\r
8583 \r
8584 \r
8585 VOID\r
8586 EchoOff()\r
8587 {\r
8588   CHARFORMAT cf;\r
8589   HWND hInput;\r
8590   consoleEcho = FALSE;\r
8591   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8592   /* This works OK: set text and background both to the same color */\r
8593   cf = consoleCF;\r
8594   cf.crTextColor = COLOR_ECHOOFF;\r
8595   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8596   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8597 }\r
8598 \r
8599 /* No Raw()...? */\r
8600 \r
8601 void Colorize(ColorClass cc, int continuation)\r
8602 {\r
8603   currentColorClass = cc;\r
8604   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8605   consoleCF.crTextColor = textAttribs[cc].color;\r
8606   consoleCF.dwEffects = textAttribs[cc].effects;\r
8607   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8608 }\r
8609 \r
8610 char *\r
8611 UserName()\r
8612 {\r
8613   static char buf[MSG_SIZ];\r
8614   DWORD bufsiz = MSG_SIZ;\r
8615 \r
8616   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8617         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8618   }\r
8619   if (!GetUserName(buf, &bufsiz)) {\r
8620     /*DisplayError("Error getting user name", GetLastError());*/\r
8621     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8622   }\r
8623   return buf;\r
8624 }\r
8625 \r
8626 char *\r
8627 HostName()\r
8628 {\r
8629   static char buf[MSG_SIZ];\r
8630   DWORD bufsiz = MSG_SIZ;\r
8631 \r
8632   if (!GetComputerName(buf, &bufsiz)) {\r
8633     /*DisplayError("Error getting host name", GetLastError());*/\r
8634     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8635   }\r
8636   return buf;\r
8637 }\r
8638 \r
8639 \r
8640 int\r
8641 ClockTimerRunning()\r
8642 {\r
8643   return clockTimerEvent != 0;\r
8644 }\r
8645 \r
8646 int\r
8647 StopClockTimer()\r
8648 {\r
8649   if (clockTimerEvent == 0) return FALSE;\r
8650   KillTimer(hwndMain, clockTimerEvent);\r
8651   clockTimerEvent = 0;\r
8652   return TRUE;\r
8653 }\r
8654 \r
8655 void\r
8656 StartClockTimer(long millisec)\r
8657 {\r
8658   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8659                              (UINT) millisec, NULL);\r
8660 }\r
8661 \r
8662 void\r
8663 DisplayWhiteClock(long timeRemaining, int highlight)\r
8664 {\r
8665   HDC hdc;\r
8666   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8667 \r
8668   if(appData.noGUI) return;\r
8669   hdc = GetDC(hwndMain);\r
8670   if (!IsIconic(hwndMain)) {\r
8671     DisplayAClock(hdc, timeRemaining, highlight, \r
8672                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8673   }\r
8674   if (highlight && iconCurrent == iconBlack) {\r
8675     iconCurrent = iconWhite;\r
8676     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8677     if (IsIconic(hwndMain)) {\r
8678       DrawIcon(hdc, 2, 2, iconCurrent);\r
8679     }\r
8680   }\r
8681   (void) ReleaseDC(hwndMain, hdc);\r
8682   if (hwndConsole)\r
8683     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8684 }\r
8685 \r
8686 void\r
8687 DisplayBlackClock(long timeRemaining, int highlight)\r
8688 {\r
8689   HDC hdc;\r
8690   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8691 \r
8692   if(appData.noGUI) return;\r
8693   hdc = GetDC(hwndMain);\r
8694   if (!IsIconic(hwndMain)) {\r
8695     DisplayAClock(hdc, timeRemaining, highlight, \r
8696                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8697   }\r
8698   if (highlight && iconCurrent == iconWhite) {\r
8699     iconCurrent = iconBlack;\r
8700     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8701     if (IsIconic(hwndMain)) {\r
8702       DrawIcon(hdc, 2, 2, iconCurrent);\r
8703     }\r
8704   }\r
8705   (void) ReleaseDC(hwndMain, hdc);\r
8706   if (hwndConsole)\r
8707     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8708 }\r
8709 \r
8710 \r
8711 int\r
8712 LoadGameTimerRunning()\r
8713 {\r
8714   return loadGameTimerEvent != 0;\r
8715 }\r
8716 \r
8717 int\r
8718 StopLoadGameTimer()\r
8719 {\r
8720   if (loadGameTimerEvent == 0) return FALSE;\r
8721   KillTimer(hwndMain, loadGameTimerEvent);\r
8722   loadGameTimerEvent = 0;\r
8723   return TRUE;\r
8724 }\r
8725 \r
8726 void\r
8727 StartLoadGameTimer(long millisec)\r
8728 {\r
8729   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8730                                 (UINT) millisec, NULL);\r
8731 }\r
8732 \r
8733 void\r
8734 AutoSaveGame()\r
8735 {\r
8736   char *defName;\r
8737   FILE *f;\r
8738   char fileTitle[MSG_SIZ];\r
8739 \r
8740   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8741   f = OpenFileDialog(hwndMain, "a", defName,\r
8742                      appData.oldSaveStyle ? "gam" : "pgn",\r
8743                      GAME_FILT, \r
8744                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8745   if (f != NULL) {\r
8746     SaveGame(f, 0, "");\r
8747     fclose(f);\r
8748   }\r
8749 }\r
8750 \r
8751 \r
8752 void\r
8753 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8754 {\r
8755   if (delayedTimerEvent != 0) {\r
8756     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8757       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8758     }\r
8759     KillTimer(hwndMain, delayedTimerEvent);\r
8760     delayedTimerEvent = 0;\r
8761     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8762     delayedTimerCallback();\r
8763   }\r
8764   delayedTimerCallback = cb;\r
8765   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8766                                 (UINT) millisec, NULL);\r
8767 }\r
8768 \r
8769 DelayedEventCallback\r
8770 GetDelayedEvent()\r
8771 {\r
8772   if (delayedTimerEvent) {\r
8773     return delayedTimerCallback;\r
8774   } else {\r
8775     return NULL;\r
8776   }\r
8777 }\r
8778 \r
8779 void\r
8780 CancelDelayedEvent()\r
8781 {\r
8782   if (delayedTimerEvent) {\r
8783     KillTimer(hwndMain, delayedTimerEvent);\r
8784     delayedTimerEvent = 0;\r
8785   }\r
8786 }\r
8787 \r
8788 DWORD GetWin32Priority(int nice)\r
8789 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8790 /*\r
8791 REALTIME_PRIORITY_CLASS     0x00000100\r
8792 HIGH_PRIORITY_CLASS         0x00000080\r
8793 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8794 NORMAL_PRIORITY_CLASS       0x00000020\r
8795 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8796 IDLE_PRIORITY_CLASS         0x00000040\r
8797 */\r
8798         if (nice < -15) return 0x00000080;\r
8799         if (nice < 0)   return 0x00008000;\r
8800         if (nice == 0)  return 0x00000020;\r
8801         if (nice < 15)  return 0x00004000;\r
8802         return 0x00000040;\r
8803 }\r
8804 \r
8805 /* Start a child process running the given program.\r
8806    The process's standard output can be read from "from", and its\r
8807    standard input can be written to "to".\r
8808    Exit with fatal error if anything goes wrong.\r
8809    Returns an opaque pointer that can be used to destroy the process\r
8810    later.\r
8811 */\r
8812 int\r
8813 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8814 {\r
8815 #define BUFSIZE 4096\r
8816 \r
8817   HANDLE hChildStdinRd, hChildStdinWr,\r
8818     hChildStdoutRd, hChildStdoutWr;\r
8819   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8820   SECURITY_ATTRIBUTES saAttr;\r
8821   BOOL fSuccess;\r
8822   PROCESS_INFORMATION piProcInfo;\r
8823   STARTUPINFO siStartInfo;\r
8824   ChildProc *cp;\r
8825   char buf[MSG_SIZ];\r
8826   DWORD err;\r
8827 \r
8828   if (appData.debugMode) {\r
8829     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8830   }\r
8831 \r
8832   *pr = NoProc;\r
8833 \r
8834   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8835   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8836   saAttr.bInheritHandle = TRUE;\r
8837   saAttr.lpSecurityDescriptor = NULL;\r
8838 \r
8839   /*\r
8840    * The steps for redirecting child's STDOUT:\r
8841    *     1. Create anonymous pipe to be STDOUT for child.\r
8842    *     2. Create a noninheritable duplicate of read handle,\r
8843    *         and close the inheritable read handle.\r
8844    */\r
8845 \r
8846   /* Create a pipe for the child's STDOUT. */\r
8847   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8848     return GetLastError();\r
8849   }\r
8850 \r
8851   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8852   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8853                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8854                              FALSE,     /* not inherited */\r
8855                              DUPLICATE_SAME_ACCESS);\r
8856   if (! fSuccess) {\r
8857     return GetLastError();\r
8858   }\r
8859   CloseHandle(hChildStdoutRd);\r
8860 \r
8861   /*\r
8862    * The steps for redirecting child's STDIN:\r
8863    *     1. Create anonymous pipe to be STDIN for child.\r
8864    *     2. Create a noninheritable duplicate of write handle,\r
8865    *         and close the inheritable write handle.\r
8866    */\r
8867 \r
8868   /* Create a pipe for the child's STDIN. */\r
8869   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8870     return GetLastError();\r
8871   }\r
8872 \r
8873   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8874   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8875                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8876                              FALSE,     /* not inherited */\r
8877                              DUPLICATE_SAME_ACCESS);\r
8878   if (! fSuccess) {\r
8879     return GetLastError();\r
8880   }\r
8881   CloseHandle(hChildStdinWr);\r
8882 \r
8883   /* Arrange to (1) look in dir for the child .exe file, and\r
8884    * (2) have dir be the child's working directory.  Interpret\r
8885    * dir relative to the directory WinBoard loaded from. */\r
8886   GetCurrentDirectory(MSG_SIZ, buf);\r
8887   SetCurrentDirectory(installDir);\r
8888   // kludgey way to update logos in tourney, as long as back-end can't do it\r
8889   if(!strcmp(cmdLine, first.program)) LoadLogo(&first, 0); else\r
8890   if(!strcmp(cmdLine, second.program)) LoadLogo(&second, 1);\r
8891   SetCurrentDirectory(dir);\r
8892 \r
8893   /* Now create the child process. */\r
8894 \r
8895   siStartInfo.cb = sizeof(STARTUPINFO);\r
8896   siStartInfo.lpReserved = NULL;\r
8897   siStartInfo.lpDesktop = NULL;\r
8898   siStartInfo.lpTitle = NULL;\r
8899   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8900   siStartInfo.cbReserved2 = 0;\r
8901   siStartInfo.lpReserved2 = NULL;\r
8902   siStartInfo.hStdInput = hChildStdinRd;\r
8903   siStartInfo.hStdOutput = hChildStdoutWr;\r
8904   siStartInfo.hStdError = hChildStdoutWr;\r
8905 \r
8906   fSuccess = CreateProcess(NULL,\r
8907                            cmdLine,        /* command line */\r
8908                            NULL,           /* process security attributes */\r
8909                            NULL,           /* primary thread security attrs */\r
8910                            TRUE,           /* handles are inherited */\r
8911                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8912                            NULL,           /* use parent's environment */\r
8913                            NULL,\r
8914                            &siStartInfo, /* STARTUPINFO pointer */\r
8915                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
8916 \r
8917   err = GetLastError();\r
8918   SetCurrentDirectory(buf); /* return to prev directory */\r
8919   if (! fSuccess) {\r
8920     return err;\r
8921   }\r
8922 \r
8923   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
8924     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
8925     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
8926   }\r
8927 \r
8928   /* Close the handles we don't need in the parent */\r
8929   CloseHandle(piProcInfo.hThread);\r
8930   CloseHandle(hChildStdinRd);\r
8931   CloseHandle(hChildStdoutWr);\r
8932 \r
8933   /* Prepare return value */\r
8934   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8935   cp->kind = CPReal;\r
8936   cp->hProcess = piProcInfo.hProcess;\r
8937   cp->pid = piProcInfo.dwProcessId;\r
8938   cp->hFrom = hChildStdoutRdDup;\r
8939   cp->hTo = hChildStdinWrDup;\r
8940 \r
8941   *pr = (void *) cp;\r
8942 \r
8943   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
8944      2000 where engines sometimes don't see the initial command(s)\r
8945      from WinBoard and hang.  I don't understand how that can happen,\r
8946      but the Sleep is harmless, so I've put it in.  Others have also\r
8947      reported what may be the same problem, so hopefully this will fix\r
8948      it for them too.  */\r
8949   Sleep(500);\r
8950 \r
8951   return NO_ERROR;\r
8952 }\r
8953 \r
8954 \r
8955 void\r
8956 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
8957 {\r
8958   ChildProc *cp; int result;\r
8959 \r
8960   cp = (ChildProc *) pr;\r
8961   if (cp == NULL) return;\r
8962 \r
8963   switch (cp->kind) {\r
8964   case CPReal:\r
8965     /* TerminateProcess is considered harmful, so... */\r
8966     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
8967     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
8968     /* The following doesn't work because the chess program\r
8969        doesn't "have the same console" as WinBoard.  Maybe\r
8970        we could arrange for this even though neither WinBoard\r
8971        nor the chess program uses a console for stdio? */\r
8972     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
8973 \r
8974     /* [AS] Special termination modes for misbehaving programs... */\r
8975     if( signal == 9 ) { \r
8976         result = TerminateProcess( cp->hProcess, 0 );\r
8977 \r
8978         if ( appData.debugMode) {\r
8979             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
8980         }\r
8981     }\r
8982     else if( signal == 10 ) {\r
8983         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
8984 \r
8985         if( dw != WAIT_OBJECT_0 ) {\r
8986             result = TerminateProcess( cp->hProcess, 0 );\r
8987 \r
8988             if ( appData.debugMode) {\r
8989                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
8990             }\r
8991 \r
8992         }\r
8993     }\r
8994 \r
8995     CloseHandle(cp->hProcess);\r
8996     break;\r
8997 \r
8998   case CPComm:\r
8999     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9000     break;\r
9001 \r
9002   case CPSock:\r
9003     closesocket(cp->sock);\r
9004     WSACleanup();\r
9005     break;\r
9006 \r
9007   case CPRcmd:\r
9008     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9009     closesocket(cp->sock);\r
9010     closesocket(cp->sock2);\r
9011     WSACleanup();\r
9012     break;\r
9013   }\r
9014   free(cp);\r
9015 }\r
9016 \r
9017 void\r
9018 InterruptChildProcess(ProcRef pr)\r
9019 {\r
9020   ChildProc *cp;\r
9021 \r
9022   cp = (ChildProc *) pr;\r
9023   if (cp == NULL) return;\r
9024   switch (cp->kind) {\r
9025   case CPReal:\r
9026     /* The following doesn't work because the chess program\r
9027        doesn't "have the same console" as WinBoard.  Maybe\r
9028        we could arrange for this even though neither WinBoard\r
9029        nor the chess program uses a console for stdio */\r
9030     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9031     break;\r
9032 \r
9033   case CPComm:\r
9034   case CPSock:\r
9035     /* Can't interrupt */\r
9036     break;\r
9037 \r
9038   case CPRcmd:\r
9039     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9040     break;\r
9041   }\r
9042 }\r
9043 \r
9044 \r
9045 int\r
9046 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9047 {\r
9048   char cmdLine[MSG_SIZ];\r
9049 \r
9050   if (port[0] == NULLCHAR) {\r
9051     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9052   } else {\r
9053     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9054   }\r
9055   return StartChildProcess(cmdLine, "", pr);\r
9056 }\r
9057 \r
9058 \r
9059 /* Code to open TCP sockets */\r
9060 \r
9061 int\r
9062 OpenTCP(char *host, char *port, ProcRef *pr)\r
9063 {\r
9064   ChildProc *cp;\r
9065   int err;\r
9066   SOCKET s;\r
9067   struct sockaddr_in sa, mysa;\r
9068   struct hostent FAR *hp;\r
9069   unsigned short uport;\r
9070   WORD wVersionRequested;\r
9071   WSADATA wsaData;\r
9072 \r
9073   /* Initialize socket DLL */\r
9074   wVersionRequested = MAKEWORD(1, 1);\r
9075   err = WSAStartup(wVersionRequested, &wsaData);\r
9076   if (err != 0) return err;\r
9077 \r
9078   /* Make socket */\r
9079   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9080     err = WSAGetLastError();\r
9081     WSACleanup();\r
9082     return err;\r
9083   }\r
9084 \r
9085   /* Bind local address using (mostly) don't-care values.\r
9086    */\r
9087   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9088   mysa.sin_family = AF_INET;\r
9089   mysa.sin_addr.s_addr = INADDR_ANY;\r
9090   uport = (unsigned short) 0;\r
9091   mysa.sin_port = htons(uport);\r
9092   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9093       == SOCKET_ERROR) {\r
9094     err = WSAGetLastError();\r
9095     WSACleanup();\r
9096     return err;\r
9097   }\r
9098 \r
9099   /* Resolve remote host name */\r
9100   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9101   if (!(hp = gethostbyname(host))) {\r
9102     unsigned int b0, b1, b2, b3;\r
9103 \r
9104     err = WSAGetLastError();\r
9105 \r
9106     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9107       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9108       hp->h_addrtype = AF_INET;\r
9109       hp->h_length = 4;\r
9110       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9111       hp->h_addr_list[0] = (char *) malloc(4);\r
9112       hp->h_addr_list[0][0] = (char) b0;\r
9113       hp->h_addr_list[0][1] = (char) b1;\r
9114       hp->h_addr_list[0][2] = (char) b2;\r
9115       hp->h_addr_list[0][3] = (char) b3;\r
9116     } else {\r
9117       WSACleanup();\r
9118       return err;\r
9119     }\r
9120   }\r
9121   sa.sin_family = hp->h_addrtype;\r
9122   uport = (unsigned short) atoi(port);\r
9123   sa.sin_port = htons(uport);\r
9124   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9125 \r
9126   /* Make connection */\r
9127   if (connect(s, (struct sockaddr *) &sa,\r
9128               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9129     err = WSAGetLastError();\r
9130     WSACleanup();\r
9131     return err;\r
9132   }\r
9133 \r
9134   /* Prepare return value */\r
9135   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9136   cp->kind = CPSock;\r
9137   cp->sock = s;\r
9138   *pr = (ProcRef *) cp;\r
9139 \r
9140   return NO_ERROR;\r
9141 }\r
9142 \r
9143 int\r
9144 OpenCommPort(char *name, ProcRef *pr)\r
9145 {\r
9146   HANDLE h;\r
9147   COMMTIMEOUTS ct;\r
9148   ChildProc *cp;\r
9149   char fullname[MSG_SIZ];\r
9150 \r
9151   if (*name != '\\')\r
9152     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9153   else\r
9154     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9155 \r
9156   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9157                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9158   if (h == (HANDLE) -1) {\r
9159     return GetLastError();\r
9160   }\r
9161   hCommPort = h;\r
9162 \r
9163   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9164 \r
9165   /* Accumulate characters until a 100ms pause, then parse */\r
9166   ct.ReadIntervalTimeout = 100;\r
9167   ct.ReadTotalTimeoutMultiplier = 0;\r
9168   ct.ReadTotalTimeoutConstant = 0;\r
9169   ct.WriteTotalTimeoutMultiplier = 0;\r
9170   ct.WriteTotalTimeoutConstant = 0;\r
9171   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9172 \r
9173   /* Prepare return value */\r
9174   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9175   cp->kind = CPComm;\r
9176   cp->hFrom = h;\r
9177   cp->hTo = h;\r
9178   *pr = (ProcRef *) cp;\r
9179 \r
9180   return NO_ERROR;\r
9181 }\r
9182 \r
9183 int\r
9184 OpenLoopback(ProcRef *pr)\r
9185 {\r
9186   DisplayFatalError(_("Not implemented"), 0, 1);\r
9187   return NO_ERROR;\r
9188 }\r
9189 \r
9190 \r
9191 int\r
9192 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9193 {\r
9194   ChildProc *cp;\r
9195   int err;\r
9196   SOCKET s, s2, s3;\r
9197   struct sockaddr_in sa, mysa;\r
9198   struct hostent FAR *hp;\r
9199   unsigned short uport;\r
9200   WORD wVersionRequested;\r
9201   WSADATA wsaData;\r
9202   int fromPort;\r
9203   char stderrPortStr[MSG_SIZ];\r
9204 \r
9205   /* Initialize socket DLL */\r
9206   wVersionRequested = MAKEWORD(1, 1);\r
9207   err = WSAStartup(wVersionRequested, &wsaData);\r
9208   if (err != 0) return err;\r
9209 \r
9210   /* Resolve remote host name */\r
9211   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9212   if (!(hp = gethostbyname(host))) {\r
9213     unsigned int b0, b1, b2, b3;\r
9214 \r
9215     err = WSAGetLastError();\r
9216 \r
9217     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9218       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9219       hp->h_addrtype = AF_INET;\r
9220       hp->h_length = 4;\r
9221       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9222       hp->h_addr_list[0] = (char *) malloc(4);\r
9223       hp->h_addr_list[0][0] = (char) b0;\r
9224       hp->h_addr_list[0][1] = (char) b1;\r
9225       hp->h_addr_list[0][2] = (char) b2;\r
9226       hp->h_addr_list[0][3] = (char) b3;\r
9227     } else {\r
9228       WSACleanup();\r
9229       return err;\r
9230     }\r
9231   }\r
9232   sa.sin_family = hp->h_addrtype;\r
9233   uport = (unsigned short) 514;\r
9234   sa.sin_port = htons(uport);\r
9235   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9236 \r
9237   /* Bind local socket to unused "privileged" port address\r
9238    */\r
9239   s = INVALID_SOCKET;\r
9240   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9241   mysa.sin_family = AF_INET;\r
9242   mysa.sin_addr.s_addr = INADDR_ANY;\r
9243   for (fromPort = 1023;; fromPort--) {\r
9244     if (fromPort < 0) {\r
9245       WSACleanup();\r
9246       return WSAEADDRINUSE;\r
9247     }\r
9248     if (s == INVALID_SOCKET) {\r
9249       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9250         err = WSAGetLastError();\r
9251         WSACleanup();\r
9252         return err;\r
9253       }\r
9254     }\r
9255     uport = (unsigned short) fromPort;\r
9256     mysa.sin_port = htons(uport);\r
9257     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9258         == SOCKET_ERROR) {\r
9259       err = WSAGetLastError();\r
9260       if (err == WSAEADDRINUSE) continue;\r
9261       WSACleanup();\r
9262       return err;\r
9263     }\r
9264     if (connect(s, (struct sockaddr *) &sa,\r
9265       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9266       err = WSAGetLastError();\r
9267       if (err == WSAEADDRINUSE) {\r
9268         closesocket(s);\r
9269         s = -1;\r
9270         continue;\r
9271       }\r
9272       WSACleanup();\r
9273       return err;\r
9274     }\r
9275     break;\r
9276   }\r
9277 \r
9278   /* Bind stderr local socket to unused "privileged" port address\r
9279    */\r
9280   s2 = INVALID_SOCKET;\r
9281   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9282   mysa.sin_family = AF_INET;\r
9283   mysa.sin_addr.s_addr = INADDR_ANY;\r
9284   for (fromPort = 1023;; fromPort--) {\r
9285     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9286     if (fromPort < 0) {\r
9287       (void) closesocket(s);\r
9288       WSACleanup();\r
9289       return WSAEADDRINUSE;\r
9290     }\r
9291     if (s2 == INVALID_SOCKET) {\r
9292       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9293         err = WSAGetLastError();\r
9294         closesocket(s);\r
9295         WSACleanup();\r
9296         return err;\r
9297       }\r
9298     }\r
9299     uport = (unsigned short) fromPort;\r
9300     mysa.sin_port = htons(uport);\r
9301     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9302         == SOCKET_ERROR) {\r
9303       err = WSAGetLastError();\r
9304       if (err == WSAEADDRINUSE) continue;\r
9305       (void) closesocket(s);\r
9306       WSACleanup();\r
9307       return err;\r
9308     }\r
9309     if (listen(s2, 1) == SOCKET_ERROR) {\r
9310       err = WSAGetLastError();\r
9311       if (err == WSAEADDRINUSE) {\r
9312         closesocket(s2);\r
9313         s2 = INVALID_SOCKET;\r
9314         continue;\r
9315       }\r
9316       (void) closesocket(s);\r
9317       (void) closesocket(s2);\r
9318       WSACleanup();\r
9319       return err;\r
9320     }\r
9321     break;\r
9322   }\r
9323   prevStderrPort = fromPort; // remember port used\r
9324   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9325 \r
9326   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9327     err = WSAGetLastError();\r
9328     (void) closesocket(s);\r
9329     (void) closesocket(s2);\r
9330     WSACleanup();\r
9331     return err;\r
9332   }\r
9333 \r
9334   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9335     err = WSAGetLastError();\r
9336     (void) closesocket(s);\r
9337     (void) closesocket(s2);\r
9338     WSACleanup();\r
9339     return err;\r
9340   }\r
9341   if (*user == NULLCHAR) user = UserName();\r
9342   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9343     err = WSAGetLastError();\r
9344     (void) closesocket(s);\r
9345     (void) closesocket(s2);\r
9346     WSACleanup();\r
9347     return err;\r
9348   }\r
9349   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9350     err = WSAGetLastError();\r
9351     (void) closesocket(s);\r
9352     (void) closesocket(s2);\r
9353     WSACleanup();\r
9354     return err;\r
9355   }\r
9356 \r
9357   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9358     err = WSAGetLastError();\r
9359     (void) closesocket(s);\r
9360     (void) closesocket(s2);\r
9361     WSACleanup();\r
9362     return err;\r
9363   }\r
9364   (void) closesocket(s2);  /* Stop listening */\r
9365 \r
9366   /* Prepare return value */\r
9367   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9368   cp->kind = CPRcmd;\r
9369   cp->sock = s;\r
9370   cp->sock2 = s3;\r
9371   *pr = (ProcRef *) cp;\r
9372 \r
9373   return NO_ERROR;\r
9374 }\r
9375 \r
9376 \r
9377 InputSourceRef\r
9378 AddInputSource(ProcRef pr, int lineByLine,\r
9379                InputCallback func, VOIDSTAR closure)\r
9380 {\r
9381   InputSource *is, *is2 = NULL;\r
9382   ChildProc *cp = (ChildProc *) pr;\r
9383 \r
9384   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9385   is->lineByLine = lineByLine;\r
9386   is->func = func;\r
9387   is->closure = closure;\r
9388   is->second = NULL;\r
9389   is->next = is->buf;\r
9390   if (pr == NoProc) {\r
9391     is->kind = CPReal;\r
9392     consoleInputSource = is;\r
9393   } else {\r
9394     is->kind = cp->kind;\r
9395     /* \r
9396         [AS] Try to avoid a race condition if the thread is given control too early:\r
9397         we create all threads suspended so that the is->hThread variable can be\r
9398         safely assigned, then let the threads start with ResumeThread.\r
9399     */\r
9400     switch (cp->kind) {\r
9401     case CPReal:\r
9402       is->hFile = cp->hFrom;\r
9403       cp->hFrom = NULL; /* now owned by InputThread */\r
9404       is->hThread =\r
9405         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9406                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9407       break;\r
9408 \r
9409     case CPComm:\r
9410       is->hFile = cp->hFrom;\r
9411       cp->hFrom = NULL; /* now owned by InputThread */\r
9412       is->hThread =\r
9413         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9414                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9415       break;\r
9416 \r
9417     case CPSock:\r
9418       is->sock = cp->sock;\r
9419       is->hThread =\r
9420         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9421                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9422       break;\r
9423 \r
9424     case CPRcmd:\r
9425       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9426       *is2 = *is;\r
9427       is->sock = cp->sock;\r
9428       is->second = is2;\r
9429       is2->sock = cp->sock2;\r
9430       is2->second = is2;\r
9431       is->hThread =\r
9432         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9433                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9434       is2->hThread =\r
9435         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9436                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9437       break;\r
9438     }\r
9439 \r
9440     if( is->hThread != NULL ) {\r
9441         ResumeThread( is->hThread );\r
9442     }\r
9443 \r
9444     if( is2 != NULL && is2->hThread != NULL ) {\r
9445         ResumeThread( is2->hThread );\r
9446     }\r
9447   }\r
9448 \r
9449   return (InputSourceRef) is;\r
9450 }\r
9451 \r
9452 void\r
9453 RemoveInputSource(InputSourceRef isr)\r
9454 {\r
9455   InputSource *is;\r
9456 \r
9457   is = (InputSource *) isr;\r
9458   is->hThread = NULL;  /* tell thread to stop */\r
9459   CloseHandle(is->hThread);\r
9460   if (is->second != NULL) {\r
9461     is->second->hThread = NULL;\r
9462     CloseHandle(is->second->hThread);\r
9463   }\r
9464 }\r
9465 \r
9466 int no_wrap(char *message, int count)\r
9467 {\r
9468     ConsoleOutput(message, count, FALSE);\r
9469     return count;\r
9470 }\r
9471 \r
9472 int\r
9473 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9474 {\r
9475   DWORD dOutCount;\r
9476   int outCount = SOCKET_ERROR;\r
9477   ChildProc *cp = (ChildProc *) pr;\r
9478   static OVERLAPPED ovl;\r
9479   static int line = 0;\r
9480 \r
9481   if (pr == NoProc)\r
9482   {\r
9483     if (appData.noJoin || !appData.useInternalWrap)\r
9484       return no_wrap(message, count);\r
9485     else\r
9486     {\r
9487       int width = get_term_width();\r
9488       int len = wrap(NULL, message, count, width, &line);\r
9489       char *msg = malloc(len);\r
9490       int dbgchk;\r
9491 \r
9492       if (!msg)\r
9493         return no_wrap(message, count);\r
9494       else\r
9495       {\r
9496         dbgchk = wrap(msg, message, count, width, &line);\r
9497         if (dbgchk != len && appData.debugMode)\r
9498             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9499         ConsoleOutput(msg, len, FALSE);\r
9500         free(msg);\r
9501         return len;\r
9502       }\r
9503     }\r
9504   }\r
9505 \r
9506   if (ovl.hEvent == NULL) {\r
9507     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9508   }\r
9509   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9510 \r
9511   switch (cp->kind) {\r
9512   case CPSock:\r
9513   case CPRcmd:\r
9514     outCount = send(cp->sock, message, count, 0);\r
9515     if (outCount == SOCKET_ERROR) {\r
9516       *outError = WSAGetLastError();\r
9517     } else {\r
9518       *outError = NO_ERROR;\r
9519     }\r
9520     break;\r
9521 \r
9522   case CPReal:\r
9523     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9524                   &dOutCount, NULL)) {\r
9525       *outError = NO_ERROR;\r
9526       outCount = (int) dOutCount;\r
9527     } else {\r
9528       *outError = GetLastError();\r
9529     }\r
9530     break;\r
9531 \r
9532   case CPComm:\r
9533     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9534                             &dOutCount, &ovl);\r
9535     if (*outError == NO_ERROR) {\r
9536       outCount = (int) dOutCount;\r
9537     }\r
9538     break;\r
9539   }\r
9540   return outCount;\r
9541 }\r
9542 \r
9543 int\r
9544 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9545                        long msdelay)\r
9546 {\r
9547   /* Ignore delay, not implemented for WinBoard */\r
9548   return OutputToProcess(pr, message, count, outError);\r
9549 }\r
9550 \r
9551 \r
9552 void\r
9553 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9554                         char *buf, int count, int error)\r
9555 {\r
9556   DisplayFatalError(_("Not implemented"), 0, 1);\r
9557 }\r
9558 \r
9559 /* see wgamelist.c for Game List functions */\r
9560 /* see wedittags.c for Edit Tags functions */\r
9561 \r
9562 \r
9563 VOID\r
9564 ICSInitScript()\r
9565 {\r
9566   FILE *f;\r
9567   char buf[MSG_SIZ];\r
9568   char *dummy;\r
9569 \r
9570   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9571     f = fopen(buf, "r");\r
9572     if (f != NULL) {\r
9573       ProcessICSInitScript(f);\r
9574       fclose(f);\r
9575     }\r
9576   }\r
9577 }\r
9578 \r
9579 \r
9580 VOID\r
9581 StartAnalysisClock()\r
9582 {\r
9583   if (analysisTimerEvent) return;\r
9584   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9585                                         (UINT) 2000, NULL);\r
9586 }\r
9587 \r
9588 VOID\r
9589 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9590 {\r
9591   highlightInfo.sq[0].x = fromX;\r
9592   highlightInfo.sq[0].y = fromY;\r
9593   highlightInfo.sq[1].x = toX;\r
9594   highlightInfo.sq[1].y = toY;\r
9595 }\r
9596 \r
9597 VOID\r
9598 ClearHighlights()\r
9599 {\r
9600   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9601     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9602 }\r
9603 \r
9604 VOID\r
9605 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9606 {\r
9607   premoveHighlightInfo.sq[0].x = fromX;\r
9608   premoveHighlightInfo.sq[0].y = fromY;\r
9609   premoveHighlightInfo.sq[1].x = toX;\r
9610   premoveHighlightInfo.sq[1].y = toY;\r
9611 }\r
9612 \r
9613 VOID\r
9614 ClearPremoveHighlights()\r
9615 {\r
9616   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9617     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9618 }\r
9619 \r
9620 VOID\r
9621 ShutDownFrontEnd()\r
9622 {\r
9623   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9624   DeleteClipboardTempFiles();\r
9625 }\r
9626 \r
9627 void\r
9628 BoardToTop()\r
9629 {\r
9630     if (IsIconic(hwndMain))\r
9631       ShowWindow(hwndMain, SW_RESTORE);\r
9632 \r
9633     SetActiveWindow(hwndMain);\r
9634 }\r
9635 \r
9636 /*\r
9637  * Prototypes for animation support routines\r
9638  */\r
9639 static void ScreenSquare(int column, int row, POINT * pt);\r
9640 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9641      POINT frames[], int * nFrames);\r
9642 \r
9643 \r
9644 #define kFactor 4\r
9645 \r
9646 void\r
9647 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9648 {       // [HGM] atomic: animate blast wave\r
9649         int i;\r
9650 \r
9651         explodeInfo.fromX = fromX;\r
9652         explodeInfo.fromY = fromY;\r
9653         explodeInfo.toX = toX;\r
9654         explodeInfo.toY = toY;\r
9655         for(i=1; i<4*kFactor; i++) {\r
9656             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9657             DrawPosition(FALSE, board);\r
9658             Sleep(appData.animSpeed);\r
9659         }\r
9660         explodeInfo.radius = 0;\r
9661         DrawPosition(TRUE, board);\r
9662 }\r
9663 \r
9664 void\r
9665 AnimateMove(board, fromX, fromY, toX, toY)\r
9666      Board board;\r
9667      int fromX;\r
9668      int fromY;\r
9669      int toX;\r
9670      int toY;\r
9671 {\r
9672   ChessSquare piece;\r
9673   POINT start, finish, mid;\r
9674   POINT frames[kFactor * 2 + 1];\r
9675   int nFrames, n;\r
9676 \r
9677   if (!appData.animate) return;\r
9678   if (doingSizing) return;\r
9679   if (fromY < 0 || fromX < 0) return;\r
9680   piece = board[fromY][fromX];\r
9681   if (piece >= EmptySquare) return;\r
9682 \r
9683   ScreenSquare(fromX, fromY, &start);\r
9684   ScreenSquare(toX, toY, &finish);\r
9685 \r
9686   /* All moves except knight jumps move in straight line */\r
9687   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9688     mid.x = start.x + (finish.x - start.x) / 2;\r
9689     mid.y = start.y + (finish.y - start.y) / 2;\r
9690   } else {\r
9691     /* Knight: make straight movement then diagonal */\r
9692     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9693        mid.x = start.x + (finish.x - start.x) / 2;\r
9694        mid.y = start.y;\r
9695      } else {\r
9696        mid.x = start.x;\r
9697        mid.y = start.y + (finish.y - start.y) / 2;\r
9698      }\r
9699   }\r
9700   \r
9701   /* Don't use as many frames for very short moves */\r
9702   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9703     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9704   else\r
9705     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9706 \r
9707   animInfo.from.x = fromX;\r
9708   animInfo.from.y = fromY;\r
9709   animInfo.to.x = toX;\r
9710   animInfo.to.y = toY;\r
9711   animInfo.lastpos = start;\r
9712   animInfo.piece = piece;\r
9713   for (n = 0; n < nFrames; n++) {\r
9714     animInfo.pos = frames[n];\r
9715     DrawPosition(FALSE, NULL);\r
9716     animInfo.lastpos = animInfo.pos;\r
9717     Sleep(appData.animSpeed);\r
9718   }\r
9719   animInfo.pos = finish;\r
9720   DrawPosition(FALSE, NULL);\r
9721   animInfo.piece = EmptySquare;\r
9722   Explode(board, fromX, fromY, toX, toY);\r
9723 }\r
9724 \r
9725 /*      Convert board position to corner of screen rect and color       */\r
9726 \r
9727 static void\r
9728 ScreenSquare(column, row, pt)\r
9729      int column; int row; POINT * pt;\r
9730 {\r
9731   if (flipView) {\r
9732     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9733     pt->y = lineGap + row * (squareSize + lineGap);\r
9734   } else {\r
9735     pt->x = lineGap + column * (squareSize + lineGap);\r
9736     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9737   }\r
9738 }\r
9739 \r
9740 /*      Generate a series of frame coords from start->mid->finish.\r
9741         The movement rate doubles until the half way point is\r
9742         reached, then halves back down to the final destination,\r
9743         which gives a nice slow in/out effect. The algorithmn\r
9744         may seem to generate too many intermediates for short\r
9745         moves, but remember that the purpose is to attract the\r
9746         viewers attention to the piece about to be moved and\r
9747         then to where it ends up. Too few frames would be less\r
9748         noticeable.                                             */\r
9749 \r
9750 static void\r
9751 Tween(start, mid, finish, factor, frames, nFrames)\r
9752      POINT * start; POINT * mid;\r
9753      POINT * finish; int factor;\r
9754      POINT frames[]; int * nFrames;\r
9755 {\r
9756   int n, fraction = 1, count = 0;\r
9757 \r
9758   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9759   for (n = 0; n < factor; n++)\r
9760     fraction *= 2;\r
9761   for (n = 0; n < factor; n++) {\r
9762     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9763     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9764     count ++;\r
9765     fraction = fraction / 2;\r
9766   }\r
9767   \r
9768   /* Midpoint */\r
9769   frames[count] = *mid;\r
9770   count ++;\r
9771   \r
9772   /* Slow out, stepping 1/2, then 1/4, ... */\r
9773   fraction = 2;\r
9774   for (n = 0; n < factor; n++) {\r
9775     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9776     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9777     count ++;\r
9778     fraction = fraction * 2;\r
9779   }\r
9780   *nFrames = count;\r
9781 }\r
9782 \r
9783 void\r
9784 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9785 {\r
9786     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9787 \r
9788     EvalGraphSet( first, last, current, pvInfoList );\r
9789 }\r
9790 \r
9791 void\r
9792 SettingsPopUp(ChessProgramState *cps)\r
9793 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9794       EngineOptionsPopup(savedHwnd, cps);\r
9795 }\r
9796 \r
9797 int flock(int fid, int code)\r
9798 {\r
9799     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
9800     OVERLAPPED ov;\r
9801     ov.hEvent = NULL;\r
9802     ov.Offset = 0;\r
9803     ov.OffsetHigh = 0;\r
9804     switch(code) {\r
9805       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
9806       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
9807       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
9808       default: return -1;\r
9809     }\r
9810     return 0;\r
9811 }\r