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