6f5e32ecf9414eba8b2adc7d01c35edc2aaa8911
[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 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 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames);\r
109 void DisplayMove P((int moveNumber));\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
111 void ChatPopUp P((char *s));\r
112 typedef struct {\r
113   ChessSquare piece;  \r
114   POINT pos;      /* window coordinates of current pos */\r
115   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
116   POINT from;     /* board coordinates of the piece's orig pos */\r
117   POINT to;       /* board coordinates of the piece's new pos */\r
118 } AnimInfo;\r
119 \r
120 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
121 \r
122 typedef struct {\r
123   POINT start;    /* window coordinates of start pos */\r
124   POINT pos;      /* window coordinates of current pos */\r
125   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
126   POINT from;     /* board coordinates of the piece's orig pos */\r
127 } DragInfo;\r
128 \r
129 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\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 HWND hCommPort = NULL;    /* currently open comm port */\r
191 static HWND hwndPause;    /* pause button */\r
192 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
193 static HBRUSH lightSquareBrush, darkSquareBrush,\r
194   blackSquareBrush, /* [HGM] for band between board and holdings */\r
195   explodeBrush,     /* [HGM] atomic */\r
196   markerBrush,      /* [HGM] markers */\r
197   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
198 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
199 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
200 static HPEN gridPen = NULL;\r
201 static HPEN highlightPen = NULL;\r
202 static HPEN premovePen = NULL;\r
203 static NPLOGPALETTE pLogPal;\r
204 static BOOL paletteChanged = FALSE;\r
205 static HICON iconWhite, iconBlack, iconCurrent;\r
206 static int doingSizing = FALSE;\r
207 static int lastSizing = 0;\r
208 static int prevStderrPort;\r
209 static HBITMAP userLogo;\r
210 \r
211 static HBITMAP liteBackTexture = NULL;\r
212 static HBITMAP darkBackTexture = NULL;\r
213 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
214 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
215 static int backTextureSquareSize = 0;\r
216 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
217 \r
218 #if __GNUC__ && !defined(_winmajor)\r
219 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
220 #else\r
221 #if defined(_winmajor)\r
222 #define oldDialog (_winmajor < 4)\r
223 #else\r
224 #define oldDialog 0\r
225 #endif\r
226 #endif\r
227 \r
228 #define INTERNATIONAL\r
229 \r
230 #ifdef INTERNATIONAL\r
231 #  define _(s) T_(s)\r
232 #  define N_(s) s\r
233 #else\r
234 #  define _(s) s\r
235 #  define N_(s) s\r
236 #  define T_(s) s\r
237 #  define Translate(x, y)\r
238 #  define LoadLanguageFile(s)\r
239 #endif\r
240 \r
241 #ifdef INTERNATIONAL\r
242 \r
243 Boolean barbaric; // flag indicating if translation is needed\r
244 \r
245 // list of item numbers used in each dialog (used to alter language at run time)\r
246 \r
247 #define ABOUTBOX -1  /* not sure why these are needed */\r
248 #define ABOUTBOX2 -1\r
249 \r
250 int dialogItems[][40] = {\r
251 { ABOUTBOX, IDOK, 400 }, \r
252 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
253   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
254 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, IDOK, IDCANCEL }, \r
255 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
256   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
257 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
258 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
259   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
260 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
261 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
262   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
263 { ABOUTBOX2, IDC_ChessBoard }, \r
264 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
265   OPT_GameListClose, IDC_GameListDoFilter }, \r
266 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
267 { DLG_Error, IDOK }, \r
268 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
269   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
270 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
271 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
272   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
273   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
274 { DLG_IndexNumber, IDC_Index }, \r
275 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
276 { DLG_TypeInName, IDOK, IDCANCEL }, \r
277 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
278   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
279 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
280   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
281   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
282   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
283   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
284   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
285   OPT_HighlightMoveArrow, OPT_AutoLogo }, \r
286 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
287   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
288   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
289   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
290   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
291   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
292   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
293   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
294   GPB_General, GPB_Alarm }, \r
295 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
296   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
297   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
298   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
299   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
300   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
301   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
302   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size }, \r
303 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
304   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
305   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
306   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
307   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
308   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
309   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat,\r
310   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
311   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
312 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
313   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
314   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,\r
315   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
316 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
317 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
318   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
319 { DLG_MoveHistory }, \r
320 { DLG_EvalGraph }, \r
321 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
322 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
323 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
324   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
325   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
326   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
327 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
328   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
329   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
330 { 0 }\r
331 };\r
332 \r
333 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
334 static int lastChecked;\r
335 static char oldLanguage[MSG_SIZ], *menuText[10][25];\r
336 extern int tinyLayout;\r
337 extern char * menuBarText[][8];\r
338 \r
339 void\r
340 LoadLanguageFile(char *name)\r
341 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
342     FILE *f;\r
343     int i=0, j=0, n=0, k;\r
344     char buf[MSG_SIZ];\r
345 \r
346     if(!name || name[0] == NULLCHAR) return;\r
347     sprintf(buf, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
348     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
349     if((f = fopen(buf, "r")) == NULL) return;\r
350     while((k = fgetc(f)) != EOF) {\r
351         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
352         languageBuf[i] = k;\r
353         if(k == '\n') {\r
354             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
355                 char *p;\r
356                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
357                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
358                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
359                         english[j] = languageBuf + n + 1; *p = 0;\r
360                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
361 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
362                     }\r
363                 }\r
364             }\r
365             n = i + 1;\r
366         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
367             switch(k) {\r
368               case 'n': k = '\n'; break;\r
369               case 'r': k = '\r'; break;\r
370               case 't': k = '\t'; break;\r
371             }\r
372             languageBuf[--i] = k;\r
373         }\r
374         i++;\r
375     }\r
376     fclose(f);\r
377     barbaric = (j != 0);\r
378     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
379 }\r
380 \r
381 char *\r
382 T_(char *s)\r
383 {   // return the translation of the given string\r
384     // efficiency can be improved a lot...\r
385     int i=0;\r
386 if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
387     if(!barbaric) return s;\r
388     if(!s) return ""; // sanity\r
389     while(english[i]) {\r
390         if(!strcmp(s, english[i])) return foreign[i];\r
391         i++;\r
392     }\r
393     return s;\r
394 }\r
395 \r
396 void\r
397 Translate(HWND hDlg, int dialogID)\r
398 {   // translate all text items in the given dialog\r
399     int i=0, j, k;\r
400     char buf[MSG_SIZ], *s;\r
401 //if(appData.debugMode) fprintf(debugFP, "Translate(%d)\n", dialogID);\r
402     if(!barbaric) return;\r
403     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
404     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
405     GetWindowText( hDlg, buf, MSG_SIZ );\r
406     s = T_(buf);\r
407 if(appData.debugMode) fprintf(debugFP, "WindowText '%s' -> '%s'\n", buf, s);\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 void\r
418 TranslateMenus(int addLanguage)\r
419 {\r
420     int i, j;\r
421     WIN32_FIND_DATA fileData;\r
422     HANDLE hFind;\r
423 #define IDM_English 1895\r
424     if(1) {\r
425         HMENU mainMenu = GetMenu(hwndMain);\r
426         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
427           HMENU subMenu = GetSubMenu(mainMenu, i);\r
428           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
429                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
430           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
431             char buf[MSG_SIZ];\r
432             UINT k = GetMenuItemID(subMenu, j);\r
433               if(menuText[i][j]) 
434                 safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) ); else {\r
435                 GetMenuString(subMenu, j, buf, MSG_SIZ, MF_BYPOSITION);\r
436                 menuText[i][j] = strdup(buf); // remember original on first change\r
437             }\r
438             if(buf[0] == NULLCHAR) continue;\r
439 //fprintf(debugFP, "menu(%d,%d) = %s (%08x, %08x) %d\n", i, j, buf, mainMenu, subMenu, k);\r
440             ModifyMenu(subMenu, j, MF_STRING|MF_BYPOSITION\r
441                                    |CheckMenuItem(subMenu, j, MF_BYPOSITION)\r
442                                    |EnableMenuItem(subMenu, j, MF_BYPOSITION), k, T_(buf));\r
443           }\r
444         }\r
445         DrawMenuBar(hwndMain);\r
446     }\r
447 \r
448     if(!addLanguage) return;\r
449     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
450         HMENU mainMenu = GetMenu(hwndMain);\r
451         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
452         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
453         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
454         i = 0; lastChecked = IDM_English;\r
455         do {\r
456             char *p, *q = fileData.cFileName;\r
457             int checkFlag = MF_UNCHECKED;\r
458             languageFile[i] = strdup(q);\r
459             if(barbaric && !strcmp(oldLanguage, q)) {\r
460                 checkFlag = MF_CHECKED;\r
461                 lastChecked = IDM_English + i + 1;\r
462                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
463             }\r
464             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
465             p = strstr(fileData.cFileName, ".lng");\r
466             if(p) *p = 0;\r
467             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
468         } while(FindNextFile(hFind, &fileData));\r
469         FindClose(hFind);\r
470     }\r
471 }\r
472 \r
473 #endif\r
474 \r
475 typedef struct {\r
476   char *name;\r
477   int squareSize;\r
478   int lineGap;\r
479   int smallLayout;\r
480   int tinyLayout;\r
481   int cliWidth, cliHeight;\r
482 } SizeInfo;\r
483 \r
484 SizeInfo sizeInfo[] = \r
485 {\r
486   { "tiny",     21, 0, 1, 1, 0, 0 },\r
487   { "teeny",    25, 1, 1, 1, 0, 0 },\r
488   { "dinky",    29, 1, 1, 1, 0, 0 },\r
489   { "petite",   33, 1, 1, 1, 0, 0 },\r
490   { "slim",     37, 2, 1, 0, 0, 0 },\r
491   { "small",    40, 2, 1, 0, 0, 0 },\r
492   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
493   { "middling", 49, 2, 0, 0, 0, 0 },\r
494   { "average",  54, 2, 0, 0, 0, 0 },\r
495   { "moderate", 58, 3, 0, 0, 0, 0 },\r
496   { "medium",   64, 3, 0, 0, 0, 0 },\r
497   { "bulky",    72, 3, 0, 0, 0, 0 },\r
498   { "large",    80, 3, 0, 0, 0, 0 },\r
499   { "big",      87, 3, 0, 0, 0, 0 },\r
500   { "huge",     95, 3, 0, 0, 0, 0 },\r
501   { "giant",    108, 3, 0, 0, 0, 0 },\r
502   { "colossal", 116, 4, 0, 0, 0, 0 },\r
503   { "titanic",  129, 4, 0, 0, 0, 0 },\r
504   { NULL, 0, 0, 0, 0, 0, 0 }\r
505 };\r
506 \r
507 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
508 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
509 {\r
510   { 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
511   { 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
512   { 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
513   { 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
514   { 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
515   { 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
516   { 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
517   { 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
518   { 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
519   { 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
520   { 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
521   { 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
522   { 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
523   { 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
524   { 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
525   { 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
526   { 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
527   { 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
528 };\r
529 \r
530 MyFont *font[NUM_SIZES][NUM_FONTS];\r
531 \r
532 typedef struct {\r
533   char *label;\r
534   int id;\r
535   HWND hwnd;\r
536   WNDPROC wndproc;\r
537 } MyButtonDesc;\r
538 \r
539 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
540 #define N_BUTTONS 5\r
541 \r
542 MyButtonDesc buttonDesc[N_BUTTONS] =\r
543 {\r
544   {"<<", IDM_ToStart, NULL, NULL},\r
545   {"<", IDM_Backward, NULL, NULL},\r
546   {"P", IDM_Pause, NULL, NULL},\r
547   {">", IDM_Forward, NULL, NULL},\r
548   {">>", IDM_ToEnd, NULL, NULL},\r
549 };\r
550 \r
551 int tinyLayout = 0, smallLayout = 0;\r
552 #define MENU_BAR_ITEMS 7\r
553 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
554   { N_("&File"), N_("&Mode"), N_("&Action"), N_("&Step"), N_("&Options"), N_("&Help"), NULL },\r
555   { N_("&F"), N_("&M"), N_("&A"), N_("&S"), N_("&O"), N_("&H"), NULL },\r
556 };\r
557 \r
558 \r
559 MySound sounds[(int)NSoundClasses];\r
560 MyTextAttribs textAttribs[(int)NColorClasses];\r
561 \r
562 MyColorizeAttribs colorizeAttribs[] = {\r
563   { (COLORREF)0, 0, N_("Shout Text") },\r
564   { (COLORREF)0, 0, N_("SShout/CShout") },\r
565   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
566   { (COLORREF)0, 0, N_("Channel Text") },\r
567   { (COLORREF)0, 0, N_("Kibitz Text") },\r
568   { (COLORREF)0, 0, N_("Tell Text") },\r
569   { (COLORREF)0, 0, N_("Challenge Text") },\r
570   { (COLORREF)0, 0, N_("Request Text") },\r
571   { (COLORREF)0, 0, N_("Seek Text") },\r
572   { (COLORREF)0, 0, N_("Normal Text") },\r
573   { (COLORREF)0, 0, N_("None") }\r
574 };\r
575 \r
576 \r
577 \r
578 static char *commentTitle;\r
579 static char *commentText;\r
580 static int commentIndex;\r
581 static Boolean editComment = FALSE;\r
582 \r
583 \r
584 char errorTitle[MSG_SIZ];\r
585 char errorMessage[2*MSG_SIZ];\r
586 HWND errorDialog = NULL;\r
587 BOOLEAN moveErrorMessageUp = FALSE;\r
588 BOOLEAN consoleEcho = TRUE;\r
589 CHARFORMAT consoleCF;\r
590 COLORREF consoleBackgroundColor;\r
591 \r
592 char *programVersion;\r
593 \r
594 #define CPReal 1\r
595 #define CPComm 2\r
596 #define CPSock 3\r
597 #define CPRcmd 4\r
598 typedef int CPKind;\r
599 \r
600 typedef struct {\r
601   CPKind kind;\r
602   HANDLE hProcess;\r
603   DWORD pid;\r
604   HANDLE hTo;\r
605   HANDLE hFrom;\r
606   SOCKET sock;\r
607   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
608 } ChildProc;\r
609 \r
610 #define INPUT_SOURCE_BUF_SIZE 4096\r
611 \r
612 typedef struct _InputSource {\r
613   CPKind kind;\r
614   HANDLE hFile;\r
615   SOCKET sock;\r
616   int lineByLine;\r
617   HANDLE hThread;\r
618   DWORD id;\r
619   char buf[INPUT_SOURCE_BUF_SIZE];\r
620   char *next;\r
621   DWORD count;\r
622   int error;\r
623   InputCallback func;\r
624   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
625   VOIDSTAR closure;\r
626 } InputSource;\r
627 \r
628 InputSource *consoleInputSource;\r
629 \r
630 DCB dcb;\r
631 \r
632 /* forward */\r
633 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
634 VOID ConsoleCreate();\r
635 LRESULT CALLBACK\r
636   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
637 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
638 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
639 VOID ParseCommSettings(char *arg, DCB *dcb);\r
640 LRESULT CALLBACK\r
641   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
642 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
643 void ParseIcsTextMenu(char *icsTextMenuString);\r
644 VOID PopUpMoveDialog(char firstchar);\r
645 VOID PopUpNameDialog(char firstchar);\r
646 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
647 \r
648 /* [AS] */\r
649 int NewGameFRC();\r
650 int GameListOptions();\r
651 \r
652 int dummy; // [HGM] for obsolete args\r
653 \r
654 HWND hwndMain = NULL;        /* root window*/\r
655 HWND hwndConsole = NULL;\r
656 HWND commentDialog = NULL;\r
657 HWND moveHistoryDialog = NULL;\r
658 HWND evalGraphDialog = NULL;\r
659 HWND engineOutputDialog = NULL;\r
660 HWND gameListDialog = NULL;\r
661 HWND editTagsDialog = NULL;\r
662 \r
663 int commentUp = FALSE;\r
664 \r
665 WindowPlacement wpMain;\r
666 WindowPlacement wpConsole;\r
667 WindowPlacement wpComment;\r
668 WindowPlacement wpMoveHistory;\r
669 WindowPlacement wpEvalGraph;\r
670 WindowPlacement wpEngineOutput;\r
671 WindowPlacement wpGameList;\r
672 WindowPlacement wpTags;\r
673 \r
674 VOID EngineOptionsPopup(); // [HGM] settings\r
675 \r
676 VOID GothicPopUp(char *title, VariantClass variant);\r
677 /*\r
678  * Setting "frozen" should disable all user input other than deleting\r
679  * the window.  We do this while engines are initializing themselves.\r
680  */\r
681 static int frozen = 0;\r
682 static int oldMenuItemState[MENU_BAR_ITEMS];\r
683 void FreezeUI()\r
684 {\r
685   HMENU hmenu;\r
686   int i;\r
687 \r
688   if (frozen) return;\r
689   frozen = 1;\r
690   hmenu = GetMenu(hwndMain);\r
691   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
692     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
693   }\r
694   DrawMenuBar(hwndMain);\r
695 }\r
696 \r
697 /* Undo a FreezeUI */\r
698 void ThawUI()\r
699 {\r
700   HMENU hmenu;\r
701   int i;\r
702 \r
703   if (!frozen) return;\r
704   frozen = 0;\r
705   hmenu = GetMenu(hwndMain);\r
706   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
707     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
708   }\r
709   DrawMenuBar(hwndMain);\r
710 }\r
711 \r
712 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
713 \r
714 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
715 #ifdef JAWS\r
716 #include "jaws.c"\r
717 #else\r
718 #define JAWS_INIT\r
719 #define JAWS_ARGS\r
720 #define JAWS_ALT_INTERCEPT\r
721 #define JAWS_KB_NAVIGATION\r
722 #define JAWS_MENU_ITEMS\r
723 #define JAWS_SILENCE\r
724 #define JAWS_REPLAY\r
725 #define JAWS_ACCEL\r
726 #define JAWS_COPYRIGHT\r
727 #define JAWS_DELETE(X) X\r
728 #define SAYMACHINEMOVE()\r
729 #define SAY(X)\r
730 #endif\r
731 \r
732 /*---------------------------------------------------------------------------*\\r
733  *\r
734  * WinMain\r
735  *\r
736 \*---------------------------------------------------------------------------*/\r
737 \r
738 int APIENTRY\r
739 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
740         LPSTR lpCmdLine, int nCmdShow)\r
741 {\r
742   MSG msg;\r
743   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
744 //  INITCOMMONCONTROLSEX ex;\r
745 \r
746   debugFP = stderr;\r
747 \r
748   LoadLibrary("RICHED32.DLL");\r
749   consoleCF.cbSize = sizeof(CHARFORMAT);\r
750 \r
751   if (!InitApplication(hInstance)) {\r
752     return (FALSE);\r
753   }\r
754   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
755     return (FALSE);\r
756   }\r
757 \r
758   JAWS_INIT\r
759 \r
760 //  InitCommonControlsEx(&ex);\r
761   InitCommonControls();\r
762 \r
763   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
764   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
765   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
766 \r
767   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
768 \r
769   while (GetMessage(&msg, /* message structure */\r
770                     NULL, /* handle of window receiving the message */\r
771                     0,    /* lowest message to examine */\r
772                     0))   /* highest message to examine */\r
773     {\r
774 \r
775       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
776         // [HGM] navigate: switch between all windows with tab\r
777         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
778         int i, currentElement = 0;\r
779 \r
780         // first determine what element of the chain we come from (if any)\r
781         if(appData.icsActive) {\r
782             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
783             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
784         }\r
785         if(engineOutputDialog && EngineOutputIsUp()) {\r
786             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
787             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
788         }\r
789         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
790             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
791         }\r
792         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
793         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
794         if(msg.hwnd == e1)                 currentElement = 2; else\r
795         if(msg.hwnd == e2)                 currentElement = 3; else\r
796         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
797         if(msg.hwnd == mh)                currentElement = 4; else\r
798         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
799         if(msg.hwnd == hText)  currentElement = 5; else\r
800         if(msg.hwnd == hInput) currentElement = 6; else\r
801         for (i = 0; i < N_BUTTONS; i++) {\r
802             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
803         }\r
804 \r
805         // determine where to go to\r
806         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
807           do {\r
808             currentElement = (currentElement + direction) % 7;\r
809             switch(currentElement) {\r
810                 case 0:\r
811                   h = hwndMain; break; // passing this case always makes the loop exit\r
812                 case 1:\r
813                   h = buttonDesc[0].hwnd; break; // could be NULL\r
814                 case 2:\r
815                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
816                   h = e1; break;\r
817                 case 3:\r
818                   if(!EngineOutputIsUp()) continue;\r
819                   h = e2; break;\r
820                 case 4:\r
821                   if(!MoveHistoryIsUp()) continue;\r
822                   h = mh; break;\r
823 //              case 6: // input to eval graph does not seem to get here!\r
824 //                if(!EvalGraphIsUp()) continue;\r
825 //                h = evalGraphDialog; break;\r
826                 case 5:\r
827                   if(!appData.icsActive) continue;\r
828                   SAY("display");\r
829                   h = hText; break;\r
830                 case 6:\r
831                   if(!appData.icsActive) continue;\r
832                   SAY("input");\r
833                   h = hInput; break;\r
834             }\r
835           } while(h == 0);\r
836 \r
837           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
838           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
839           SetFocus(h);\r
840 \r
841           continue; // this message now has been processed\r
842         }\r
843       }\r
844 \r
845       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
846           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
847           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
848           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
849           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
850           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
851           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
852           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
853           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
854           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
855         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
856         for(i=0; i<MAX_CHAT; i++) \r
857             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
858                 done = 1; break;\r
859         }\r
860         if(done) continue; // [HGM] chat: end patch\r
861         TranslateMessage(&msg); /* Translates virtual key codes */\r
862         DispatchMessage(&msg);  /* Dispatches message to window */\r
863       }\r
864     }\r
865 \r
866 \r
867   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
868 }\r
869 \r
870 /*---------------------------------------------------------------------------*\\r
871  *\r
872  * Initialization functions\r
873  *\r
874 \*---------------------------------------------------------------------------*/\r
875 \r
876 void\r
877 SetUserLogo()\r
878 {   // update user logo if necessary\r
879     static char oldUserName[MSG_SIZ], *curName;\r
880 \r
881     if(appData.autoLogo) {\r
882           curName = UserName();\r
883           if(strcmp(curName, oldUserName)) {\r
884                 sprintf(oldUserName, "logos\\%s.bmp", curName);\r
885                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
886                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
887           }\r
888     }\r
889 }\r
890 \r
891 BOOL\r
892 InitApplication(HINSTANCE hInstance)\r
893 {\r
894   WNDCLASS wc;\r
895 \r
896   /* Fill in window class structure with parameters that describe the */\r
897   /* main window. */\r
898 \r
899   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
900   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
901   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
902   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
903   wc.hInstance     = hInstance;         /* Owner of this class */\r
904   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
905   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
906   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
907   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
908   wc.lpszClassName = szAppName;                 /* Name to register as */\r
909 \r
910   /* Register the window class and return success/failure code. */\r
911   if (!RegisterClass(&wc)) return FALSE;\r
912 \r
913   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
914   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
915   wc.cbClsExtra    = 0;\r
916   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
917   wc.hInstance     = hInstance;\r
918   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
919   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
920   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
921   wc.lpszMenuName  = NULL;\r
922   wc.lpszClassName = szConsoleName;\r
923 \r
924   if (!RegisterClass(&wc)) return FALSE;\r
925   return TRUE;\r
926 }\r
927 \r
928 \r
929 /* Set by InitInstance, used by EnsureOnScreen */\r
930 int screenHeight, screenWidth;\r
931 \r
932 void\r
933 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
934 {\r
935 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
936   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
937   if (*x > screenWidth - 32) *x = 0;\r
938   if (*y > screenHeight - 32) *y = 0;\r
939   if (*x < minX) *x = minX;\r
940   if (*y < minY) *y = minY;\r
941 }\r
942 \r
943 BOOL\r
944 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
945 {\r
946   HWND hwnd; /* Main window handle. */\r
947   int ibs;\r
948   WINDOWPLACEMENT wp;\r
949   char *filepart;\r
950 \r
951   hInst = hInstance;    /* Store instance handle in our global variable */\r
952   programName = szAppName;\r
953 \r
954   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
955     *filepart = NULLCHAR;\r
956   } else {\r
957     GetCurrentDirectory(MSG_SIZ, installDir);\r
958   }\r
959   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
960   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
961   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
962   /* xboard, and older WinBoards, controlled the move sound with the\r
963      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
964      always turn the option on (so that the backend will call us),\r
965      then let the user turn the sound off by setting it to silence if\r
966      desired.  To accommodate old winboard.ini files saved by old\r
967      versions of WinBoard, we also turn off the sound if the option\r
968      was initially set to false. [HGM] taken out of InitAppData */\r
969   if (!appData.ringBellAfterMoves) {\r
970     sounds[(int)SoundMove].name = strdup("");\r
971     appData.ringBellAfterMoves = TRUE;\r
972   }\r
973   if (appData.debugMode) {\r
974     debugFP = fopen(appData.nameOfDebugFile, "w");\r
975     setbuf(debugFP, NULL);\r
976   }\r
977 \r
978   LoadLanguageFile(appData.language);\r
979 \r
980   InitBackEnd1();\r
981 \r
982 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
983 //  InitEngineUCI( installDir, &second );\r
984 \r
985   /* Create a main window for this application instance. */\r
986   hwnd = CreateWindow(szAppName, szTitle,\r
987                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
988                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
989                       NULL, NULL, hInstance, NULL);\r
990   hwndMain = hwnd;\r
991 \r
992   /* If window could not be created, return "failure" */\r
993   if (!hwnd) {\r
994     return (FALSE);\r
995   }\r
996 \r
997   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
998   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
999       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1000 \r
1001       if (first.programLogo == NULL && appData.debugMode) {\r
1002           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
1003       }\r
1004   } else if(appData.autoLogo) {\r
1005       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
1006         char buf[MSG_SIZ];\r
1007         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
1008         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
1009       }\r
1010   }\r
1011 \r
1012   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
1013       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1014 \r
1015       if (second.programLogo == NULL && appData.debugMode) {\r
1016           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
1017       }\r
1018   } else if(appData.autoLogo) {\r
1019       char buf[MSG_SIZ];\r
1020       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
1021         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1022         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1023       } else\r
1024       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
1025         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
1026         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
1027       }\r
1028   }\r
1029 \r
1030   SetUserLogo();\r
1031 \r
1032   iconWhite = LoadIcon(hInstance, "icon_white");\r
1033   iconBlack = LoadIcon(hInstance, "icon_black");\r
1034   iconCurrent = iconWhite;\r
1035   InitDrawingColors();\r
1036   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1037   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1038   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1039     /* Compute window size for each board size, and use the largest\r
1040        size that fits on this screen as the default. */\r
1041     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1042     if (boardSize == (BoardSize)-1 &&\r
1043         winH <= screenHeight\r
1044            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1045         && winW <= screenWidth) {\r
1046       boardSize = (BoardSize)ibs;\r
1047     }\r
1048   }\r
1049 \r
1050   InitDrawingSizes(boardSize, 0);\r
1051   TranslateMenus(1);\r
1052   InitMenuChecks();\r
1053   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1054 \r
1055   /* [AS] Load textures if specified */\r
1056   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1057   \r
1058   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1059       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1060       liteBackTextureMode = appData.liteBackTextureMode;\r
1061 \r
1062       if (liteBackTexture == NULL && appData.debugMode) {\r
1063           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1064       }\r
1065   }\r
1066   \r
1067   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1068       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1069       darkBackTextureMode = appData.darkBackTextureMode;\r
1070 \r
1071       if (darkBackTexture == NULL && appData.debugMode) {\r
1072           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1073       }\r
1074   }\r
1075 \r
1076   mysrandom( (unsigned) time(NULL) );\r
1077 \r
1078   /* [AS] Restore layout */\r
1079   if( wpMoveHistory.visible ) {\r
1080       MoveHistoryPopUp();\r
1081   }\r
1082 \r
1083   if( wpEvalGraph.visible ) {\r
1084       EvalGraphPopUp();\r
1085   }\r
1086 \r
1087   if( wpEngineOutput.visible ) {\r
1088       EngineOutputPopUp();\r
1089   }\r
1090 \r
1091   /* Make the window visible; update its client area; and return "success" */\r
1092   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1093   wp.length = sizeof(WINDOWPLACEMENT);\r
1094   wp.flags = 0;\r
1095   wp.showCmd = nCmdShow;\r
1096   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1097   wp.rcNormalPosition.left = wpMain.x;\r
1098   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1099   wp.rcNormalPosition.top = wpMain.y;\r
1100   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1101   SetWindowPlacement(hwndMain, &wp);\r
1102 \r
1103   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1104 \r
1105   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1106                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1107 \r
1108   if (hwndConsole) {\r
1109 #if AOT_CONSOLE\r
1110     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1111                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1112 #endif\r
1113     ShowWindow(hwndConsole, nCmdShow);\r
1114     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
1115       char buf[MSG_SIZ], *p = buf, *q;\r
1116         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
1117       do {\r
1118         q = strchr(p, ';');\r
1119         if(q) *q++ = 0;\r
1120         if(*p) ChatPopUp(p);\r
1121       } while(p=q);\r
1122     }\r
1123     SetActiveWindow(hwndConsole);\r
1124   }\r
1125   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1126   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1127 \r
1128   return TRUE;\r
1129 \r
1130 }\r
1131 \r
1132 VOID\r
1133 InitMenuChecks()\r
1134 {\r
1135   HMENU hmenu = GetMenu(hwndMain);\r
1136 \r
1137   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1138                         MF_BYCOMMAND|((appData.icsActive &&\r
1139                                        *appData.icsCommPort != NULLCHAR) ?\r
1140                                       MF_ENABLED : MF_GRAYED));\r
1141   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1142                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1143                                      MF_CHECKED : MF_UNCHECKED));\r
1144 }\r
1145 \r
1146 //---------------------------------------------------------------------------------------------------------\r
1147 \r
1148 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1149 #define XBOARD FALSE\r
1150 \r
1151 #define OPTCHAR "/"\r
1152 #define SEPCHAR "="\r
1153 \r
1154 #include "args.h"\r
1155 \r
1156 // front-end part of option handling\r
1157 \r
1158 VOID\r
1159 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1160 {\r
1161   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1162   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1163   DeleteDC(hdc);\r
1164   lf->lfWidth = 0;\r
1165   lf->lfEscapement = 0;\r
1166   lf->lfOrientation = 0;\r
1167   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1168   lf->lfItalic = mfp->italic;\r
1169   lf->lfUnderline = mfp->underline;\r
1170   lf->lfStrikeOut = mfp->strikeout;\r
1171   lf->lfCharSet = mfp->charset;\r
1172   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1173   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1174   lf->lfQuality = DEFAULT_QUALITY;\r
1175   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1176     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1177 }\r
1178 \r
1179 void\r
1180 CreateFontInMF(MyFont *mf)\r
1181\r
1182   LFfromMFP(&mf->lf, &mf->mfp);\r
1183   if (mf->hf) DeleteObject(mf->hf);\r
1184   mf->hf = CreateFontIndirect(&mf->lf);\r
1185 }\r
1186 \r
1187 // [HGM] This platform-dependent table provides the location for storing the color info\r
1188 void *\r
1189 colorVariable[] = {\r
1190   &whitePieceColor, \r
1191   &blackPieceColor, \r
1192   &lightSquareColor,\r
1193   &darkSquareColor, \r
1194   &highlightSquareColor,\r
1195   &premoveHighlightColor,\r
1196   NULL,\r
1197   &consoleBackgroundColor,\r
1198   &appData.fontForeColorWhite,\r
1199   &appData.fontBackColorWhite,\r
1200   &appData.fontForeColorBlack,\r
1201   &appData.fontBackColorBlack,\r
1202   &appData.evalHistColorWhite,\r
1203   &appData.evalHistColorBlack,\r
1204   &appData.highlightArrowColor,\r
1205 };\r
1206 \r
1207 /* Command line font name parser.  NULL name means do nothing.\r
1208    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1209    For backward compatibility, syntax without the colon is also\r
1210    accepted, but font names with digits in them won't work in that case.\r
1211 */\r
1212 VOID\r
1213 ParseFontName(char *name, MyFontParams *mfp)\r
1214 {\r
1215   char *p, *q;\r
1216   if (name == NULL) return;\r
1217   p = name;\r
1218   q = strchr(p, ':');\r
1219   if (q) {\r
1220     if (q - p >= sizeof(mfp->faceName))\r
1221       ExitArgError(_("Font name too long:"), name);\r
1222     memcpy(mfp->faceName, p, q - p);\r
1223     mfp->faceName[q - p] = NULLCHAR;\r
1224     p = q + 1;\r
1225   } else {\r
1226     q = mfp->faceName;\r
1227     while (*p && !isdigit(*p)) {\r
1228       *q++ = *p++;\r
1229       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1230         ExitArgError(_("Font name too long:"), name);\r
1231     }\r
1232     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1233     *q = NULLCHAR;\r
1234   }\r
1235   if (!*p) ExitArgError(_("Font point size missing:"), name);\r
1236   mfp->pointSize = (float) atof(p);\r
1237   mfp->bold = (strchr(p, 'b') != NULL);\r
1238   mfp->italic = (strchr(p, 'i') != NULL);\r
1239   mfp->underline = (strchr(p, 'u') != NULL);\r
1240   mfp->strikeout = (strchr(p, 's') != NULL);\r
1241   mfp->charset = DEFAULT_CHARSET;\r
1242   q = strchr(p, 'c');\r
1243   if (q)\r
1244     mfp->charset = (BYTE) atoi(q+1);\r
1245 }\r
1246 \r
1247 void\r
1248 ParseFont(char *name, int number)\r
1249 { // wrapper to shield back-end from 'font'\r
1250   ParseFontName(name, &font[boardSize][number]->mfp);\r
1251 }\r
1252 \r
1253 void\r
1254 SetFontDefaults()\r
1255 { // in WB  we have a 2D array of fonts; this initializes their description\r
1256   int i, j;\r
1257   /* Point font array elements to structures and\r
1258      parse default font names */\r
1259   for (i=0; i<NUM_FONTS; i++) {\r
1260     for (j=0; j<NUM_SIZES; j++) {\r
1261       font[j][i] = &fontRec[j][i];\r
1262       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1263     }\r
1264   }\r
1265 }\r
1266 \r
1267 void\r
1268 CreateFonts()\r
1269 { // here we create the actual fonts from the selected descriptions\r
1270   int i, j;\r
1271   for (i=0; i<NUM_FONTS; i++) {\r
1272     for (j=0; j<NUM_SIZES; j++) {\r
1273       CreateFontInMF(font[j][i]);\r
1274     }\r
1275   }\r
1276 }\r
1277 /* Color name parser.\r
1278    X version accepts X color names, but this one\r
1279    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1280 COLORREF\r
1281 ParseColorName(char *name)\r
1282 {\r
1283   int red, green, blue, count;\r
1284   char buf[MSG_SIZ];\r
1285 \r
1286   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1287   if (count != 3) {\r
1288     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1289       &red, &green, &blue);\r
1290   }\r
1291   if (count != 3) {\r
1292     sprintf(buf, _("Can't parse color name %s"), name);\r
1293     DisplayError(buf, 0);\r
1294     return RGB(0, 0, 0);\r
1295   }\r
1296   return PALETTERGB(red, green, blue);\r
1297 }\r
1298 \r
1299 void\r
1300 ParseColor(int n, char *name)\r
1301 { // for WinBoard the color is an int, which needs to be derived from the string\r
1302   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1303 }\r
1304 \r
1305 void\r
1306 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1307 {\r
1308   char *e = argValue;\r
1309   int eff = 0;\r
1310 \r
1311   while (*e) {\r
1312     if (*e == 'b')      eff |= CFE_BOLD;\r
1313     else if (*e == 'i') eff |= CFE_ITALIC;\r
1314     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1315     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1316     else if (*e == '#' || isdigit(*e)) break;\r
1317     e++;\r
1318   }\r
1319   *effects = eff;\r
1320   *color   = ParseColorName(e);\r
1321 }\r
1322 \r
1323 void\r
1324 ParseTextAttribs(ColorClass cc, char *s)\r
1325 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1326     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1327     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1328 }\r
1329 \r
1330 void\r
1331 ParseBoardSize(void *addr, char *name)\r
1332 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1333   BoardSize bs = SizeTiny;\r
1334   while (sizeInfo[bs].name != NULL) {\r
1335     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1336         *(BoardSize *)addr = bs;\r
1337         return;\r
1338     }\r
1339     bs++;\r
1340   }\r
1341   ExitArgError(_("Unrecognized board size value"), name);\r
1342 }\r
1343 \r
1344 void\r
1345 LoadAllSounds()\r
1346 { // [HGM] import name from appData first\r
1347   ColorClass cc;\r
1348   SoundClass sc;\r
1349   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1350     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1351     textAttribs[cc].sound.data = NULL;\r
1352     MyLoadSound(&textAttribs[cc].sound);\r
1353   }\r
1354   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1355     textAttribs[cc].sound.name = strdup("");\r
1356     textAttribs[cc].sound.data = NULL;\r
1357   }\r
1358   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1359     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1360     sounds[sc].data = NULL;\r
1361     MyLoadSound(&sounds[sc]);\r
1362   }\r
1363 }\r
1364 \r
1365 void\r
1366 SetCommPortDefaults()\r
1367 {\r
1368    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1369   dcb.DCBlength = sizeof(DCB);\r
1370   dcb.BaudRate = 9600;\r
1371   dcb.fBinary = TRUE;\r
1372   dcb.fParity = FALSE;\r
1373   dcb.fOutxCtsFlow = FALSE;\r
1374   dcb.fOutxDsrFlow = FALSE;\r
1375   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1376   dcb.fDsrSensitivity = FALSE;\r
1377   dcb.fTXContinueOnXoff = TRUE;\r
1378   dcb.fOutX = FALSE;\r
1379   dcb.fInX = FALSE;\r
1380   dcb.fNull = FALSE;\r
1381   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1382   dcb.fAbortOnError = FALSE;\r
1383   dcb.ByteSize = 7;\r
1384   dcb.Parity = SPACEPARITY;\r
1385   dcb.StopBits = ONESTOPBIT;\r
1386 }\r
1387 \r
1388 // [HGM] args: these three cases taken out to stay in front-end\r
1389 void\r
1390 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1391 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1392         // while the curent board size determines the element. This system should be ported to XBoard.\r
1393         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1394         int bs;\r
1395         for (bs=0; bs<NUM_SIZES; bs++) {\r
1396           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1397           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1398           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1399             ad->argName, mfp->faceName, mfp->pointSize,\r
1400             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1401             mfp->bold ? "b" : "",\r
1402             mfp->italic ? "i" : "",\r
1403             mfp->underline ? "u" : "",\r
1404             mfp->strikeout ? "s" : "",\r
1405             (int)mfp->charset);\r
1406         }\r
1407       }\r
1408 \r
1409 void\r
1410 ExportSounds()\r
1411 { // [HGM] copy the names from the internal WB variables to appData\r
1412   ColorClass cc;\r
1413   SoundClass sc;\r
1414   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1415     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1416   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1417     (&appData.soundMove)[sc] = sounds[sc].name;\r
1418 }\r
1419 \r
1420 void\r
1421 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1422 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1423         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1424         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1425           (ta->effects & CFE_BOLD) ? "b" : "",\r
1426           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1427           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1428           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1429           (ta->effects) ? " " : "",\r
1430           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1431       }\r
1432 \r
1433 void\r
1434 SaveColor(FILE *f, ArgDescriptor *ad)\r
1435 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1436         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1437         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1438           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1439 }\r
1440 \r
1441 void\r
1442 SaveBoardSize(FILE *f, char *name, void *addr)\r
1443 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1444   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1445 }\r
1446 \r
1447 void\r
1448 ParseCommPortSettings(char *s)\r
1449 { // wrapper to keep dcb from back-end\r
1450   ParseCommSettings(s, &dcb);\r
1451 }\r
1452 \r
1453 void\r
1454 GetWindowCoords()\r
1455 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1456   GetActualPlacement(hwndMain, &wpMain);\r
1457   GetActualPlacement(hwndConsole, &wpConsole);\r
1458   GetActualPlacement(commentDialog, &wpComment);\r
1459   GetActualPlacement(editTagsDialog, &wpTags);\r
1460   GetActualPlacement(gameListDialog, &wpGameList);\r
1461   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1462   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1463   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1464 }\r
1465 \r
1466 void\r
1467 PrintCommPortSettings(FILE *f, char *name)\r
1468 { // wrapper to shield back-end from DCB\r
1469       PrintCommSettings(f, name, &dcb);\r
1470 }\r
1471 \r
1472 int\r
1473 MySearchPath(char *installDir, char *name, char *fullname)\r
1474 {\r
1475   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1476   if(name[0]== '%') {\r
1477     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1478     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1479       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1480       *strchr(buf, '%') = 0;\r
1481       strcat(fullname, getenv(buf));\r
1482       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1483     }\r
1484     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1485     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1486     return (int) strlen(fullname);\r
1487   }\r
1488   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1489 }\r
1490 \r
1491 int\r
1492 MyGetFullPathName(char *name, char *fullname)\r
1493 {\r
1494   char *dummy;\r
1495   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1496 }\r
1497 \r
1498 int\r
1499 MainWindowUp()\r
1500 { // [HGM] args: allows testing if main window is realized from back-end\r
1501   return hwndMain != NULL;\r
1502 }\r
1503 \r
1504 void\r
1505 PopUpStartupDialog()\r
1506 {\r
1507     FARPROC lpProc;\r
1508     \r
1509     LoadLanguageFile(appData.language);\r
1510     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1511     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1512     FreeProcInstance(lpProc);\r
1513 }\r
1514 \r
1515 /*---------------------------------------------------------------------------*\\r
1516  *\r
1517  * GDI board drawing routines\r
1518  *\r
1519 \*---------------------------------------------------------------------------*/\r
1520 \r
1521 /* [AS] Draw square using background texture */\r
1522 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1523 {\r
1524     XFORM   x;\r
1525 \r
1526     if( mode == 0 ) {\r
1527         return; /* Should never happen! */\r
1528     }\r
1529 \r
1530     SetGraphicsMode( dst, GM_ADVANCED );\r
1531 \r
1532     switch( mode ) {\r
1533     case 1:\r
1534         /* Identity */\r
1535         break;\r
1536     case 2:\r
1537         /* X reflection */\r
1538         x.eM11 = -1.0;\r
1539         x.eM12 = 0;\r
1540         x.eM21 = 0;\r
1541         x.eM22 = 1.0;\r
1542         x.eDx = (FLOAT) dw + dx - 1;\r
1543         x.eDy = 0;\r
1544         dx = 0;\r
1545         SetWorldTransform( dst, &x );\r
1546         break;\r
1547     case 3:\r
1548         /* Y reflection */\r
1549         x.eM11 = 1.0;\r
1550         x.eM12 = 0;\r
1551         x.eM21 = 0;\r
1552         x.eM22 = -1.0;\r
1553         x.eDx = 0;\r
1554         x.eDy = (FLOAT) dh + dy - 1;\r
1555         dy = 0;\r
1556         SetWorldTransform( dst, &x );\r
1557         break;\r
1558     case 4:\r
1559         /* X/Y flip */\r
1560         x.eM11 = 0;\r
1561         x.eM12 = 1.0;\r
1562         x.eM21 = 1.0;\r
1563         x.eM22 = 0;\r
1564         x.eDx = (FLOAT) dx;\r
1565         x.eDy = (FLOAT) dy;\r
1566         dx = 0;\r
1567         dy = 0;\r
1568         SetWorldTransform( dst, &x );\r
1569         break;\r
1570     }\r
1571 \r
1572     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1573 \r
1574     x.eM11 = 1.0;\r
1575     x.eM12 = 0;\r
1576     x.eM21 = 0;\r
1577     x.eM22 = 1.0;\r
1578     x.eDx = 0;\r
1579     x.eDy = 0;\r
1580     SetWorldTransform( dst, &x );\r
1581 \r
1582     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1583 }\r
1584 \r
1585 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1586 enum {\r
1587     PM_WP = (int) WhitePawn, \r
1588     PM_WN = (int) WhiteKnight, \r
1589     PM_WB = (int) WhiteBishop, \r
1590     PM_WR = (int) WhiteRook, \r
1591     PM_WQ = (int) WhiteQueen, \r
1592     PM_WF = (int) WhiteFerz, \r
1593     PM_WW = (int) WhiteWazir, \r
1594     PM_WE = (int) WhiteAlfil, \r
1595     PM_WM = (int) WhiteMan, \r
1596     PM_WO = (int) WhiteCannon, \r
1597     PM_WU = (int) WhiteUnicorn, \r
1598     PM_WH = (int) WhiteNightrider, \r
1599     PM_WA = (int) WhiteAngel, \r
1600     PM_WC = (int) WhiteMarshall, \r
1601     PM_WAB = (int) WhiteCardinal, \r
1602     PM_WD = (int) WhiteDragon, \r
1603     PM_WL = (int) WhiteLance, \r
1604     PM_WS = (int) WhiteCobra, \r
1605     PM_WV = (int) WhiteFalcon, \r
1606     PM_WSG = (int) WhiteSilver, \r
1607     PM_WG = (int) WhiteGrasshopper, \r
1608     PM_WK = (int) WhiteKing,\r
1609     PM_BP = (int) BlackPawn, \r
1610     PM_BN = (int) BlackKnight, \r
1611     PM_BB = (int) BlackBishop, \r
1612     PM_BR = (int) BlackRook, \r
1613     PM_BQ = (int) BlackQueen, \r
1614     PM_BF = (int) BlackFerz, \r
1615     PM_BW = (int) BlackWazir, \r
1616     PM_BE = (int) BlackAlfil, \r
1617     PM_BM = (int) BlackMan,\r
1618     PM_BO = (int) BlackCannon, \r
1619     PM_BU = (int) BlackUnicorn, \r
1620     PM_BH = (int) BlackNightrider, \r
1621     PM_BA = (int) BlackAngel, \r
1622     PM_BC = (int) BlackMarshall, \r
1623     PM_BG = (int) BlackGrasshopper, \r
1624     PM_BAB = (int) BlackCardinal,\r
1625     PM_BD = (int) BlackDragon,\r
1626     PM_BL = (int) BlackLance,\r
1627     PM_BS = (int) BlackCobra,\r
1628     PM_BV = (int) BlackFalcon,\r
1629     PM_BSG = (int) BlackSilver,\r
1630     PM_BK = (int) BlackKing\r
1631 };\r
1632 \r
1633 static HFONT hPieceFont = NULL;\r
1634 static HBITMAP hPieceMask[(int) EmptySquare];\r
1635 static HBITMAP hPieceFace[(int) EmptySquare];\r
1636 static int fontBitmapSquareSize = 0;\r
1637 static char pieceToFontChar[(int) EmptySquare] =\r
1638                               { 'p', 'n', 'b', 'r', 'q', \r
1639                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1640                       'k', 'o', 'm', 'v', 't', 'w', \r
1641                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1642                                                               'l' };\r
1643 \r
1644 extern BOOL SetCharTable( char *table, const char * map );\r
1645 /* [HGM] moved to backend.c */\r
1646 \r
1647 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1648 {\r
1649     HBRUSH hbrush;\r
1650     BYTE r1 = GetRValue( color );\r
1651     BYTE g1 = GetGValue( color );\r
1652     BYTE b1 = GetBValue( color );\r
1653     BYTE r2 = r1 / 2;\r
1654     BYTE g2 = g1 / 2;\r
1655     BYTE b2 = b1 / 2;\r
1656     RECT rc;\r
1657 \r
1658     /* Create a uniform background first */\r
1659     hbrush = CreateSolidBrush( color );\r
1660     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1661     FillRect( hdc, &rc, hbrush );\r
1662     DeleteObject( hbrush );\r
1663     \r
1664     if( mode == 1 ) {\r
1665         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1666         int steps = squareSize / 2;\r
1667         int i;\r
1668 \r
1669         for( i=0; i<steps; i++ ) {\r
1670             BYTE r = r1 - (r1-r2) * i / steps;\r
1671             BYTE g = g1 - (g1-g2) * i / steps;\r
1672             BYTE b = b1 - (b1-b2) * i / steps;\r
1673 \r
1674             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1675             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1676             FillRect( hdc, &rc, hbrush );\r
1677             DeleteObject(hbrush);\r
1678         }\r
1679     }\r
1680     else if( mode == 2 ) {\r
1681         /* Diagonal gradient, good more or less for every piece */\r
1682         POINT triangle[3];\r
1683         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1684         HBRUSH hbrush_old;\r
1685         int steps = squareSize;\r
1686         int i;\r
1687 \r
1688         triangle[0].x = squareSize - steps;\r
1689         triangle[0].y = squareSize;\r
1690         triangle[1].x = squareSize;\r
1691         triangle[1].y = squareSize;\r
1692         triangle[2].x = squareSize;\r
1693         triangle[2].y = squareSize - steps;\r
1694 \r
1695         for( i=0; i<steps; i++ ) {\r
1696             BYTE r = r1 - (r1-r2) * i / steps;\r
1697             BYTE g = g1 - (g1-g2) * i / steps;\r
1698             BYTE b = b1 - (b1-b2) * i / steps;\r
1699 \r
1700             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1701             hbrush_old = SelectObject( hdc, hbrush );\r
1702             Polygon( hdc, triangle, 3 );\r
1703             SelectObject( hdc, hbrush_old );\r
1704             DeleteObject(hbrush);\r
1705             triangle[0].x++;\r
1706             triangle[2].y++;\r
1707         }\r
1708 \r
1709         SelectObject( hdc, hpen );\r
1710     }\r
1711 }\r
1712 \r
1713 /*\r
1714     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1715     seems to work ok. The main problem here is to find the "inside" of a chess\r
1716     piece: follow the steps as explained below.\r
1717 */\r
1718 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1719 {\r
1720     HBITMAP hbm;\r
1721     HBITMAP hbm_old;\r
1722     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1723     RECT rc;\r
1724     SIZE sz;\r
1725     POINT pt;\r
1726     int backColor = whitePieceColor; \r
1727     int foreColor = blackPieceColor;\r
1728     \r
1729     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1730         backColor = appData.fontBackColorWhite;\r
1731         foreColor = appData.fontForeColorWhite;\r
1732     }\r
1733     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1734         backColor = appData.fontBackColorBlack;\r
1735         foreColor = appData.fontForeColorBlack;\r
1736     }\r
1737 \r
1738     /* Mask */\r
1739     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1740 \r
1741     hbm_old = SelectObject( hdc, hbm );\r
1742 \r
1743     rc.left = 0;\r
1744     rc.top = 0;\r
1745     rc.right = squareSize;\r
1746     rc.bottom = squareSize;\r
1747 \r
1748     /* Step 1: background is now black */\r
1749     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1750 \r
1751     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1752 \r
1753     pt.x = (squareSize - sz.cx) / 2;\r
1754     pt.y = (squareSize - sz.cy) / 2;\r
1755 \r
1756     SetBkMode( hdc, TRANSPARENT );\r
1757     SetTextColor( hdc, chroma );\r
1758     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1759     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1760 \r
1761     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1762     /* Step 3: the area outside the piece is filled with white */\r
1763 //    FloodFill( hdc, 0, 0, chroma );\r
1764     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1765     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1766     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1767     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1768     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1769     /* \r
1770         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1771         but if the start point is not inside the piece we're lost!\r
1772         There should be a better way to do this... if we could create a region or path\r
1773         from the fill operation we would be fine for example.\r
1774     */\r
1775 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1776     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1777 \r
1778     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1779         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1780         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1781 \r
1782         SelectObject( dc2, bm2 );\r
1783         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1784         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1785         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1786         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1787         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1788 \r
1789         DeleteDC( dc2 );\r
1790         DeleteObject( bm2 );\r
1791     }\r
1792 \r
1793     SetTextColor( hdc, 0 );\r
1794     /* \r
1795         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1796         draw the piece again in black for safety.\r
1797     */\r
1798     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1799 \r
1800     SelectObject( hdc, hbm_old );\r
1801 \r
1802     if( hPieceMask[index] != NULL ) {\r
1803         DeleteObject( hPieceMask[index] );\r
1804     }\r
1805 \r
1806     hPieceMask[index] = hbm;\r
1807 \r
1808     /* Face */\r
1809     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1810 \r
1811     SelectObject( hdc, hbm );\r
1812 \r
1813     {\r
1814         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1815         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1816         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1817 \r
1818         SelectObject( dc1, hPieceMask[index] );\r
1819         SelectObject( dc2, bm2 );\r
1820         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1821         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1822         \r
1823         /* \r
1824             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1825             the piece background and deletes (makes transparent) the rest.\r
1826             Thanks to that mask, we are free to paint the background with the greates\r
1827             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1828             We use this, to make gradients and give the pieces a "roundish" look.\r
1829         */\r
1830         SetPieceBackground( hdc, backColor, 2 );\r
1831         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1832 \r
1833         DeleteDC( dc2 );\r
1834         DeleteDC( dc1 );\r
1835         DeleteObject( bm2 );\r
1836     }\r
1837 \r
1838     SetTextColor( hdc, foreColor );\r
1839     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1840 \r
1841     SelectObject( hdc, hbm_old );\r
1842 \r
1843     if( hPieceFace[index] != NULL ) {\r
1844         DeleteObject( hPieceFace[index] );\r
1845     }\r
1846 \r
1847     hPieceFace[index] = hbm;\r
1848 }\r
1849 \r
1850 static int TranslatePieceToFontPiece( int piece )\r
1851 {\r
1852     switch( piece ) {\r
1853     case BlackPawn:\r
1854         return PM_BP;\r
1855     case BlackKnight:\r
1856         return PM_BN;\r
1857     case BlackBishop:\r
1858         return PM_BB;\r
1859     case BlackRook:\r
1860         return PM_BR;\r
1861     case BlackQueen:\r
1862         return PM_BQ;\r
1863     case BlackKing:\r
1864         return PM_BK;\r
1865     case WhitePawn:\r
1866         return PM_WP;\r
1867     case WhiteKnight:\r
1868         return PM_WN;\r
1869     case WhiteBishop:\r
1870         return PM_WB;\r
1871     case WhiteRook:\r
1872         return PM_WR;\r
1873     case WhiteQueen:\r
1874         return PM_WQ;\r
1875     case WhiteKing:\r
1876         return PM_WK;\r
1877 \r
1878     case BlackAngel:\r
1879         return PM_BA;\r
1880     case BlackMarshall:\r
1881         return PM_BC;\r
1882     case BlackFerz:\r
1883         return PM_BF;\r
1884     case BlackNightrider:\r
1885         return PM_BH;\r
1886     case BlackAlfil:\r
1887         return PM_BE;\r
1888     case BlackWazir:\r
1889         return PM_BW;\r
1890     case BlackUnicorn:\r
1891         return PM_BU;\r
1892     case BlackCannon:\r
1893         return PM_BO;\r
1894     case BlackGrasshopper:\r
1895         return PM_BG;\r
1896     case BlackMan:\r
1897         return PM_BM;\r
1898     case BlackSilver:\r
1899         return PM_BSG;\r
1900     case BlackLance:\r
1901         return PM_BL;\r
1902     case BlackFalcon:\r
1903         return PM_BV;\r
1904     case BlackCobra:\r
1905         return PM_BS;\r
1906     case BlackCardinal:\r
1907         return PM_BAB;\r
1908     case BlackDragon:\r
1909         return PM_BD;\r
1910 \r
1911     case WhiteAngel:\r
1912         return PM_WA;\r
1913     case WhiteMarshall:\r
1914         return PM_WC;\r
1915     case WhiteFerz:\r
1916         return PM_WF;\r
1917     case WhiteNightrider:\r
1918         return PM_WH;\r
1919     case WhiteAlfil:\r
1920         return PM_WE;\r
1921     case WhiteWazir:\r
1922         return PM_WW;\r
1923     case WhiteUnicorn:\r
1924         return PM_WU;\r
1925     case WhiteCannon:\r
1926         return PM_WO;\r
1927     case WhiteGrasshopper:\r
1928         return PM_WG;\r
1929     case WhiteMan:\r
1930         return PM_WM;\r
1931     case WhiteSilver:\r
1932         return PM_WSG;\r
1933     case WhiteLance:\r
1934         return PM_WL;\r
1935     case WhiteFalcon:\r
1936         return PM_WV;\r
1937     case WhiteCobra:\r
1938         return PM_WS;\r
1939     case WhiteCardinal:\r
1940         return PM_WAB;\r
1941     case WhiteDragon:\r
1942         return PM_WD;\r
1943     }\r
1944 \r
1945     return 0;\r
1946 }\r
1947 \r
1948 void CreatePiecesFromFont()\r
1949 {\r
1950     LOGFONT lf;\r
1951     HDC hdc_window = NULL;\r
1952     HDC hdc = NULL;\r
1953     HFONT hfont_old;\r
1954     int fontHeight;\r
1955     int i;\r
1956 \r
1957     if( fontBitmapSquareSize < 0 ) {\r
1958         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
1959         return;\r
1960     }\r
1961 \r
1962     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
1963         fontBitmapSquareSize = -1;\r
1964         return;\r
1965     }\r
1966 \r
1967     if( fontBitmapSquareSize != squareSize ) {\r
1968         hdc_window = GetDC( hwndMain );\r
1969         hdc = CreateCompatibleDC( hdc_window );\r
1970 \r
1971         if( hPieceFont != NULL ) {\r
1972             DeleteObject( hPieceFont );\r
1973         }\r
1974         else {\r
1975             for( i=0; i<=(int)BlackKing; i++ ) {\r
1976                 hPieceMask[i] = NULL;\r
1977                 hPieceFace[i] = NULL;\r
1978             }\r
1979         }\r
1980 \r
1981         fontHeight = 75;\r
1982 \r
1983         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
1984             fontHeight = appData.fontPieceSize;\r
1985         }\r
1986 \r
1987         fontHeight = (fontHeight * squareSize) / 100;\r
1988 \r
1989         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
1990         lf.lfWidth = 0;\r
1991         lf.lfEscapement = 0;\r
1992         lf.lfOrientation = 0;\r
1993         lf.lfWeight = FW_NORMAL;\r
1994         lf.lfItalic = 0;\r
1995         lf.lfUnderline = 0;\r
1996         lf.lfStrikeOut = 0;\r
1997         lf.lfCharSet = DEFAULT_CHARSET;\r
1998         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1999         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2000         lf.lfQuality = PROOF_QUALITY;\r
2001         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2002         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2003         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2004 \r
2005         hPieceFont = CreateFontIndirect( &lf );\r
2006 \r
2007         if( hPieceFont == NULL ) {\r
2008             fontBitmapSquareSize = -2;\r
2009         }\r
2010         else {\r
2011             /* Setup font-to-piece character table */\r
2012             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2013                 /* No (or wrong) global settings, try to detect the font */\r
2014                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2015                     /* Alpha */\r
2016                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2017                 }\r
2018                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2019                     /* DiagramTT* family */\r
2020                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2021                 }\r
2022                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2023                     /* Fairy symbols */\r
2024                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2025                 }\r
2026                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2027                     /* Good Companion (Some characters get warped as literal :-( */\r
2028                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2029                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2030                     SetCharTable(pieceToFontChar, s);\r
2031                 }\r
2032                 else {\r
2033                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2034                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2035                 }\r
2036             }\r
2037 \r
2038             /* Create bitmaps */\r
2039             hfont_old = SelectObject( hdc, hPieceFont );\r
2040             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2041                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2042                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2043 \r
2044             SelectObject( hdc, hfont_old );\r
2045 \r
2046             fontBitmapSquareSize = squareSize;\r
2047         }\r
2048     }\r
2049 \r
2050     if( hdc != NULL ) {\r
2051         DeleteDC( hdc );\r
2052     }\r
2053 \r
2054     if( hdc_window != NULL ) {\r
2055         ReleaseDC( hwndMain, hdc_window );\r
2056     }\r
2057 }\r
2058 \r
2059 HBITMAP\r
2060 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2061 {\r
2062   char name[128];\r
2063 \r
2064   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
2065   if (gameInfo.event &&\r
2066       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2067       strcmp(name, "k80s") == 0) {\r
2068     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2069   }\r
2070   return LoadBitmap(hinst, name);\r
2071 }\r
2072 \r
2073 \r
2074 /* Insert a color into the program's logical palette\r
2075    structure.  This code assumes the given color is\r
2076    the result of the RGB or PALETTERGB macro, and it\r
2077    knows how those macros work (which is documented).\r
2078 */\r
2079 VOID\r
2080 InsertInPalette(COLORREF color)\r
2081 {\r
2082   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2083 \r
2084   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2085     DisplayFatalError(_("Too many colors"), 0, 1);\r
2086     pLogPal->palNumEntries--;\r
2087     return;\r
2088   }\r
2089 \r
2090   pe->peFlags = (char) 0;\r
2091   pe->peRed = (char) (0xFF & color);\r
2092   pe->peGreen = (char) (0xFF & (color >> 8));\r
2093   pe->peBlue = (char) (0xFF & (color >> 16));\r
2094   return;\r
2095 }\r
2096 \r
2097 \r
2098 VOID\r
2099 InitDrawingColors()\r
2100 {\r
2101   if (pLogPal == NULL) {\r
2102     /* Allocate enough memory for a logical palette with\r
2103      * PALETTESIZE entries and set the size and version fields\r
2104      * of the logical palette structure.\r
2105      */\r
2106     pLogPal = (NPLOGPALETTE)\r
2107       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2108                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2109     pLogPal->palVersion    = 0x300;\r
2110   }\r
2111   pLogPal->palNumEntries = 0;\r
2112 \r
2113   InsertInPalette(lightSquareColor);\r
2114   InsertInPalette(darkSquareColor);\r
2115   InsertInPalette(whitePieceColor);\r
2116   InsertInPalette(blackPieceColor);\r
2117   InsertInPalette(highlightSquareColor);\r
2118   InsertInPalette(premoveHighlightColor);\r
2119 \r
2120   /*  create a logical color palette according the information\r
2121    *  in the LOGPALETTE structure.\r
2122    */\r
2123   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2124 \r
2125   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2126   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2127   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2128   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2129   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2130   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2131   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2132   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2133   /* [AS] Force rendering of the font-based pieces */\r
2134   if( fontBitmapSquareSize > 0 ) {\r
2135     fontBitmapSquareSize = 0;\r
2136   }\r
2137 }\r
2138 \r
2139 \r
2140 int\r
2141 BoardWidth(int boardSize, int n)\r
2142 { /* [HGM] argument n added to allow different width and height */\r
2143   int lineGap = sizeInfo[boardSize].lineGap;\r
2144 \r
2145   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2146       lineGap = appData.overrideLineGap;\r
2147   }\r
2148 \r
2149   return (n + 1) * lineGap +\r
2150           n * sizeInfo[boardSize].squareSize;\r
2151 }\r
2152 \r
2153 /* Respond to board resize by dragging edge */\r
2154 VOID\r
2155 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2156 {\r
2157   BoardSize newSize = NUM_SIZES - 1;\r
2158   static int recurse = 0;\r
2159   if (IsIconic(hwndMain)) return;\r
2160   if (recurse > 0) return;\r
2161   recurse++;\r
2162   while (newSize > 0) {\r
2163         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2164         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2165            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2166     newSize--;\r
2167   } \r
2168   boardSize = newSize;\r
2169   InitDrawingSizes(boardSize, flags);\r
2170   recurse--;\r
2171 }\r
2172 \r
2173 \r
2174 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2175 \r
2176 VOID\r
2177 InitDrawingSizes(BoardSize boardSize, int flags)\r
2178 {\r
2179   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2180   ChessSquare piece;\r
2181   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2182   HDC hdc;\r
2183   SIZE clockSize, messageSize;\r
2184   HFONT oldFont;\r
2185   char buf[MSG_SIZ];\r
2186   char *str;\r
2187   HMENU hmenu = GetMenu(hwndMain);\r
2188   RECT crect, wrect, oldRect;\r
2189   int offby;\r
2190   LOGBRUSH logbrush;\r
2191 \r
2192   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2193   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2194 \r
2195   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2196   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2197 \r
2198   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2199   oldRect.top = wpMain.y;\r
2200   oldRect.right = wpMain.x + wpMain.width;\r
2201   oldRect.bottom = wpMain.y + wpMain.height;\r
2202 \r
2203   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2204   smallLayout = sizeInfo[boardSize].smallLayout;\r
2205   squareSize = sizeInfo[boardSize].squareSize;\r
2206   lineGap = sizeInfo[boardSize].lineGap;\r
2207   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2208 \r
2209   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2210       lineGap = appData.overrideLineGap;\r
2211   }\r
2212 \r
2213   if (tinyLayout != oldTinyLayout) {\r
2214     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
2215     if (tinyLayout) {\r
2216       style &= ~WS_SYSMENU;\r
2217       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2218                  "&Minimize\tCtrl+F4");\r
2219     } else {\r
2220       style |= WS_SYSMENU;\r
2221       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2222     }\r
2223     SetWindowLong(hwndMain, GWL_STYLE, style);\r
2224 \r
2225     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2226       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2227         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2228     }\r
2229     DrawMenuBar(hwndMain);\r
2230   }\r
2231 \r
2232   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2233   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2234 \r
2235   /* Get text area sizes */\r
2236   hdc = GetDC(hwndMain);\r
2237   if (appData.clockMode) {\r
2238     sprintf(buf, _("White: %s"), TimeString(23*60*60*1000L));\r
2239   } else {\r
2240     sprintf(buf, _("White"));\r
2241   }\r
2242   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2243   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2244   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2245   str = _("We only care about the height here");\r
2246   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2247   SelectObject(hdc, oldFont);\r
2248   ReleaseDC(hwndMain, hdc);\r
2249 \r
2250   /* Compute where everything goes */\r
2251   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2252         /* [HGM] logo: if either logo is on, reserve space for it */\r
2253         logoHeight =  2*clockSize.cy;\r
2254         leftLogoRect.left   = OUTER_MARGIN;\r
2255         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2256         leftLogoRect.top    = OUTER_MARGIN;\r
2257         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2258 \r
2259         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2260         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2261         rightLogoRect.top    = OUTER_MARGIN;\r
2262         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2263 \r
2264 \r
2265     whiteRect.left = leftLogoRect.right;\r
2266     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2267     whiteRect.top = OUTER_MARGIN;\r
2268     whiteRect.bottom = whiteRect.top + logoHeight;\r
2269 \r
2270     blackRect.right = rightLogoRect.left;\r
2271     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2272     blackRect.top = whiteRect.top;\r
2273     blackRect.bottom = whiteRect.bottom;\r
2274   } else {\r
2275     whiteRect.left = OUTER_MARGIN;\r
2276     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2277     whiteRect.top = OUTER_MARGIN;\r
2278     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2279 \r
2280     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2281     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2282     blackRect.top = whiteRect.top;\r
2283     blackRect.bottom = whiteRect.bottom;\r
2284 \r
2285     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2286   }\r
2287 \r
2288   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2289   if (appData.showButtonBar) {\r
2290     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2291       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2292   } else {\r
2293     messageRect.right = OUTER_MARGIN + boardWidth;\r
2294   }\r
2295   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2296   messageRect.bottom = messageRect.top + messageSize.cy;\r
2297 \r
2298   boardRect.left = OUTER_MARGIN;\r
2299   boardRect.right = boardRect.left + boardWidth;\r
2300   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2301   boardRect.bottom = boardRect.top + boardHeight;\r
2302 \r
2303   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2304   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2305   oldBoardSize = boardSize;\r
2306   oldTinyLayout = tinyLayout;\r
2307   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2308   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2309     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2310   winW *= 1 + twoBoards;\r
2311   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2312   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2313   wpMain.height = winH; //       without disturbing window attachments\r
2314   GetWindowRect(hwndMain, &wrect);\r
2315   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2316                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2317 \r
2318   // [HGM] placement: let attached windows follow size change.\r
2319   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2320   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2321   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2322   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2323   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2324 \r
2325   /* compensate if menu bar wrapped */\r
2326   GetClientRect(hwndMain, &crect);\r
2327   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2328   wpMain.height += offby;\r
2329   switch (flags) {\r
2330   case WMSZ_TOPLEFT:\r
2331     SetWindowPos(hwndMain, NULL, \r
2332                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2333                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2334     break;\r
2335 \r
2336   case WMSZ_TOPRIGHT:\r
2337   case WMSZ_TOP:\r
2338     SetWindowPos(hwndMain, NULL, \r
2339                  wrect.left, wrect.bottom - wpMain.height, \r
2340                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2341     break;\r
2342 \r
2343   case WMSZ_BOTTOMLEFT:\r
2344   case WMSZ_LEFT:\r
2345     SetWindowPos(hwndMain, NULL, \r
2346                  wrect.right - wpMain.width, wrect.top, \r
2347                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2348     break;\r
2349 \r
2350   case WMSZ_BOTTOMRIGHT:\r
2351   case WMSZ_BOTTOM:\r
2352   case WMSZ_RIGHT:\r
2353   default:\r
2354     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2355                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2356     break;\r
2357   }\r
2358 \r
2359   hwndPause = NULL;\r
2360   for (i = 0; i < N_BUTTONS; i++) {\r
2361     if (buttonDesc[i].hwnd != NULL) {\r
2362       DestroyWindow(buttonDesc[i].hwnd);\r
2363       buttonDesc[i].hwnd = NULL;\r
2364     }\r
2365     if (appData.showButtonBar) {\r
2366       buttonDesc[i].hwnd =\r
2367         CreateWindow("BUTTON", buttonDesc[i].label,\r
2368                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2369                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2370                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2371                      (HMENU) buttonDesc[i].id,\r
2372                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
2373       if (tinyLayout) {\r
2374         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2375                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2376                     MAKELPARAM(FALSE, 0));\r
2377       }\r
2378       if (buttonDesc[i].id == IDM_Pause)\r
2379         hwndPause = buttonDesc[i].hwnd;\r
2380       buttonDesc[i].wndproc = (WNDPROC)\r
2381         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
2382     }\r
2383   }\r
2384   if (gridPen != NULL) DeleteObject(gridPen);\r
2385   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2386   if (premovePen != NULL) DeleteObject(premovePen);\r
2387   if (lineGap != 0) {\r
2388     logbrush.lbStyle = BS_SOLID;\r
2389     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2390     gridPen =\r
2391       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2392                    lineGap, &logbrush, 0, NULL);\r
2393     logbrush.lbColor = highlightSquareColor;\r
2394     highlightPen =\r
2395       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2396                    lineGap, &logbrush, 0, NULL);\r
2397 \r
2398     logbrush.lbColor = premoveHighlightColor; \r
2399     premovePen =\r
2400       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2401                    lineGap, &logbrush, 0, NULL);\r
2402 \r
2403     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2404     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2405       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2406       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2407         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2408       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2409         BOARD_WIDTH * (squareSize + lineGap);\r
2410       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2411     }\r
2412     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2413       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2414       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2415         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2416         lineGap / 2 + (i * (squareSize + lineGap));\r
2417       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2418         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2419       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2420     }\r
2421   }\r
2422 \r
2423   /* [HGM] Licensing requirement */\r
2424 #ifdef GOTHIC\r
2425   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2426 #endif\r
2427 #ifdef FALCON\r
2428   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2429 #endif\r
2430   GothicPopUp( "", VariantNormal);\r
2431 \r
2432 \r
2433 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2434 \r
2435   /* Load piece bitmaps for this board size */\r
2436   for (i=0; i<=2; i++) {\r
2437     for (piece = WhitePawn;\r
2438          (int) piece < (int) BlackPawn;\r
2439          piece = (ChessSquare) ((int) piece + 1)) {\r
2440       if (pieceBitmap[i][piece] != NULL)\r
2441         DeleteObject(pieceBitmap[i][piece]);\r
2442     }\r
2443   }\r
2444 \r
2445   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2446   // Orthodox Chess pieces\r
2447   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2448   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2449   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2450   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2451   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2452   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2453   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2454   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2455   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2456   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2457   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2458   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2459   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2460   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2461   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2462   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
2463     // in Shogi, Hijack the unused Queen for Lance\r
2464     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2465     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2466     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2467   } else {\r
2468     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2469     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2470     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2471   }\r
2472 \r
2473   if(squareSize <= 72 && squareSize >= 33) { \r
2474     /* A & C are available in most sizes now */\r
2475     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2476       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2477       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2478       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2479       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2480       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2481       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2482       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2483       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2484       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2485       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2486       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2487       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2488     } else { // Smirf-like\r
2489       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2490       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2491       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2492     }\r
2493     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2494       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2495       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2496       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2497     } else { // WinBoard standard\r
2498       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2499       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2500       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2501     }\r
2502   }\r
2503 \r
2504 \r
2505   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2506     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2507     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2508     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2509     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2510     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2511     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2512     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2513     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2514     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2515     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2516     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2517     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2518     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2519     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2520     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2521     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2522     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2523     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2524     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2525     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2526     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2527     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2528     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2529     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2530     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2531     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2532     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2533     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2534     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2535     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2536 \r
2537     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2538       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2539       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2540       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2541       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2542       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2543       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2544       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2545       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2546       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2547       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2548       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2549       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2550     } else {\r
2551       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2552       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2553       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2554       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2555       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2556       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2557       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2558       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2559       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2560       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2561       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2562       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2563     }\r
2564 \r
2565   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2566     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2567     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2568     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2569     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2570     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2571     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2572     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2573     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2574     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2575     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2576     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2577     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2578     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2579     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2580   }\r
2581 \r
2582 \r
2583   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2584   /* special Shogi support in this size */\r
2585   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2586       for (piece = WhitePawn;\r
2587            (int) piece < (int) BlackPawn;\r
2588            piece = (ChessSquare) ((int) piece + 1)) {\r
2589         if (pieceBitmap[i][piece] != NULL)\r
2590           DeleteObject(pieceBitmap[i][piece]);\r
2591       }\r
2592     }\r
2593   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2594   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2595   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2596   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2597   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2598   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2599   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2600   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2601   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2602   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2603   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2604   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2605   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2606   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2607   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2608   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2609   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2610   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2611   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2612   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2613   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2614   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2615   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2616   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2617   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2618   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2619   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2620   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2621   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2622   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2623   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2624   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2625   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2626   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2627   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2628   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2629   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2630   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2631   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2632   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2633   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2634   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2635   minorSize = 0;\r
2636   }\r
2637 }\r
2638 \r
2639 HBITMAP\r
2640 PieceBitmap(ChessSquare p, int kind)\r
2641 {\r
2642   if ((int) p >= (int) BlackPawn)\r
2643     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2644 \r
2645   return pieceBitmap[kind][(int) p];\r
2646 }\r
2647 \r
2648 /***************************************************************/\r
2649 \r
2650 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2651 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2652 /*\r
2653 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2654 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2655 */\r
2656 \r
2657 VOID\r
2658 SquareToPos(int row, int column, int * x, int * y)\r
2659 {\r
2660   if (flipView) {\r
2661     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2662     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2663   } else {\r
2664     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2665     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2666   }\r
2667 }\r
2668 \r
2669 VOID\r
2670 DrawCoordsOnDC(HDC hdc)\r
2671 {\r
2672   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
2673   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
2674   char str[2] = { NULLCHAR, NULLCHAR };\r
2675   int oldMode, oldAlign, x, y, start, i;\r
2676   HFONT oldFont;\r
2677   HBRUSH oldBrush;\r
2678 \r
2679   if (!appData.showCoords)\r
2680     return;\r
2681 \r
2682   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
2683 \r
2684   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2685   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2686   oldAlign = GetTextAlign(hdc);\r
2687   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2688 \r
2689   y = boardRect.top + lineGap;\r
2690   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2691 \r
2692   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2693   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2694     str[0] = files[start + i];\r
2695     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2696     y += squareSize + lineGap;\r
2697   }\r
2698 \r
2699   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
2700 \r
2701   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2702   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2703     str[0] = ranks[start + i];\r
2704     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2705     x += squareSize + lineGap;\r
2706   }    \r
2707 \r
2708   SelectObject(hdc, oldBrush);\r
2709   SetBkMode(hdc, oldMode);\r
2710   SetTextAlign(hdc, oldAlign);\r
2711   SelectObject(hdc, oldFont);\r
2712 }\r
2713 \r
2714 VOID\r
2715 DrawGridOnDC(HDC hdc)\r
2716 {\r
2717   HPEN oldPen;\r
2718  \r
2719   if (lineGap != 0) {\r
2720     oldPen = SelectObject(hdc, gridPen);\r
2721     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2722     SelectObject(hdc, oldPen);\r
2723   }\r
2724 }\r
2725 \r
2726 #define HIGHLIGHT_PEN 0\r
2727 #define PREMOVE_PEN   1\r
2728 \r
2729 VOID\r
2730 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2731 {\r
2732   int x1, y1;\r
2733   HPEN oldPen, hPen;\r
2734   if (lineGap == 0) return;\r
2735   if (flipView) {\r
2736     x1 = boardRect.left +\r
2737       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2738     y1 = boardRect.top +\r
2739       lineGap/2 + y * (squareSize + lineGap);\r
2740   } else {\r
2741     x1 = boardRect.left +\r
2742       lineGap/2 + x * (squareSize + lineGap);\r
2743     y1 = boardRect.top +\r
2744       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2745   }\r
2746   hPen = pen ? premovePen : highlightPen;\r
2747   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2748   MoveToEx(hdc, x1, y1, NULL);\r
2749   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2750   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2751   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2752   LineTo(hdc, x1, y1);\r
2753   SelectObject(hdc, oldPen);\r
2754 }\r
2755 \r
2756 VOID\r
2757 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2758 {\r
2759   int i;\r
2760   for (i=0; i<2; i++) {\r
2761     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2762       DrawHighlightOnDC(hdc, TRUE,\r
2763                         h->sq[i].x, h->sq[i].y,\r
2764                         pen);\r
2765   }\r
2766 }\r
2767 \r
2768 /* Note: sqcolor is used only in monoMode */\r
2769 /* Note that this code is largely duplicated in woptions.c,\r
2770    function DrawSampleSquare, so that needs to be updated too */\r
2771 VOID\r
2772 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2773 {\r
2774   HBITMAP oldBitmap;\r
2775   HBRUSH oldBrush;\r
2776   int tmpSize;\r
2777 \r
2778   if (appData.blindfold) return;\r
2779 \r
2780   /* [AS] Use font-based pieces if needed */\r
2781   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2782     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2783     CreatePiecesFromFont();\r
2784 \r
2785     if( fontBitmapSquareSize == squareSize ) {\r
2786         int index = TranslatePieceToFontPiece(piece);\r
2787 \r
2788         SelectObject( tmphdc, hPieceMask[ index ] );\r
2789 \r
2790       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2791         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2792       else\r
2793         BitBlt( hdc,\r
2794             x, y,\r
2795             squareSize, squareSize,\r
2796             tmphdc,\r
2797             0, 0,\r
2798             SRCAND );\r
2799 \r
2800         SelectObject( tmphdc, hPieceFace[ index ] );\r
2801 \r
2802       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2803         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2804       else\r
2805         BitBlt( hdc,\r
2806             x, y,\r
2807             squareSize, squareSize,\r
2808             tmphdc,\r
2809             0, 0,\r
2810             SRCPAINT );\r
2811 \r
2812         return;\r
2813     }\r
2814   }\r
2815 \r
2816   if (appData.monoMode) {\r
2817     SelectObject(tmphdc, PieceBitmap(piece, \r
2818       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2819     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2820            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2821   } else {\r
2822     tmpSize = squareSize;\r
2823     if(minorSize &&\r
2824         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2825          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2826       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2827       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2828       x += (squareSize - minorSize)>>1;\r
2829       y += squareSize - minorSize - 2;\r
2830       tmpSize = minorSize;\r
2831     }\r
2832     if (color || appData.allWhite ) {\r
2833       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2834       if( color )\r
2835               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2836       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2837       if(appData.upsideDown && color==flipView)\r
2838         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2839       else\r
2840         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2841       /* Use black for outline of white pieces */\r
2842       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2843       if(appData.upsideDown && color==flipView)\r
2844         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2845       else\r
2846         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2847     } else {\r
2848       /* Use square color for details of black pieces */\r
2849       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2850       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2851       if(appData.upsideDown && !flipView)\r
2852         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2853       else\r
2854         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2855     }\r
2856     SelectObject(hdc, oldBrush);\r
2857     SelectObject(tmphdc, oldBitmap);\r
2858   }\r
2859 }\r
2860 \r
2861 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2862 int GetBackTextureMode( int algo )\r
2863 {\r
2864     int result = BACK_TEXTURE_MODE_DISABLED;\r
2865 \r
2866     switch( algo ) \r
2867     {\r
2868         case BACK_TEXTURE_MODE_PLAIN:\r
2869             result = 1; /* Always use identity map */\r
2870             break;\r
2871         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2872             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2873             break;\r
2874     }\r
2875 \r
2876     return result;\r
2877 }\r
2878 \r
2879 /* \r
2880     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2881     to handle redraws cleanly (as random numbers would always be different).\r
2882 */\r
2883 VOID RebuildTextureSquareInfo()\r
2884 {\r
2885     BITMAP bi;\r
2886     int lite_w = 0;\r
2887     int lite_h = 0;\r
2888     int dark_w = 0;\r
2889     int dark_h = 0;\r
2890     int row;\r
2891     int col;\r
2892 \r
2893     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2894 \r
2895     if( liteBackTexture != NULL ) {\r
2896         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2897             lite_w = bi.bmWidth;\r
2898             lite_h = bi.bmHeight;\r
2899         }\r
2900     }\r
2901 \r
2902     if( darkBackTexture != NULL ) {\r
2903         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2904             dark_w = bi.bmWidth;\r
2905             dark_h = bi.bmHeight;\r
2906         }\r
2907     }\r
2908 \r
2909     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2910         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2911             if( (col + row) & 1 ) {\r
2912                 /* Lite square */\r
2913                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2914                   if( lite_w >= squareSize*BOARD_WIDTH )\r
2915                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
2916                   else\r
2917                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2918                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
2919                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
2920                   else\r
2921                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2922                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2923                 }\r
2924             }\r
2925             else {\r
2926                 /* Dark square */\r
2927                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
2928                   if( dark_w >= squareSize*BOARD_WIDTH )\r
2929                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
2930                   else\r
2931                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
2932                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
2933                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
2934                   else\r
2935                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
2936                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
2937                 }\r
2938             }\r
2939         }\r
2940     }\r
2941 }\r
2942 \r
2943 /* [AS] Arrow highlighting support */\r
2944 \r
2945 static int A_WIDTH = 5; /* Width of arrow body */\r
2946 \r
2947 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
2948 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
2949 \r
2950 static double Sqr( double x )\r
2951 {\r
2952     return x*x;\r
2953 }\r
2954 \r
2955 static int Round( double x )\r
2956 {\r
2957     return (int) (x + 0.5);\r
2958 }\r
2959 \r
2960 /* Draw an arrow between two points using current settings */\r
2961 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
2962 {\r
2963     POINT arrow[7];\r
2964     double dx, dy, j, k, x, y;\r
2965 \r
2966     if( d_x == s_x ) {\r
2967         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2968 \r
2969         arrow[0].x = s_x + A_WIDTH;\r
2970         arrow[0].y = s_y;\r
2971 \r
2972         arrow[1].x = s_x + A_WIDTH;\r
2973         arrow[1].y = d_y - h;\r
2974 \r
2975         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
2976         arrow[2].y = d_y - h;\r
2977 \r
2978         arrow[3].x = d_x;\r
2979         arrow[3].y = d_y;\r
2980 \r
2981         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
2982         arrow[4].y = d_y - h;\r
2983 \r
2984         arrow[5].x = s_x - A_WIDTH;\r
2985         arrow[5].y = d_y - h;\r
2986 \r
2987         arrow[6].x = s_x - A_WIDTH;\r
2988         arrow[6].y = s_y;\r
2989     }\r
2990     else if( d_y == s_y ) {\r
2991         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2992 \r
2993         arrow[0].x = s_x;\r
2994         arrow[0].y = s_y + A_WIDTH;\r
2995 \r
2996         arrow[1].x = d_x - w;\r
2997         arrow[1].y = s_y + A_WIDTH;\r
2998 \r
2999         arrow[2].x = d_x - w;\r
3000         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3001 \r
3002         arrow[3].x = d_x;\r
3003         arrow[3].y = d_y;\r
3004 \r
3005         arrow[4].x = d_x - w;\r
3006         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3007 \r
3008         arrow[5].x = d_x - w;\r
3009         arrow[5].y = s_y - A_WIDTH;\r
3010 \r
3011         arrow[6].x = s_x;\r
3012         arrow[6].y = s_y - A_WIDTH;\r
3013     }\r
3014     else {\r
3015         /* [AS] Needed a lot of paper for this! :-) */\r
3016         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3017         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3018   \r
3019         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3020 \r
3021         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3022 \r
3023         x = s_x;\r
3024         y = s_y;\r
3025 \r
3026         arrow[0].x = Round(x - j);\r
3027         arrow[0].y = Round(y + j*dx);\r
3028 \r
3029         arrow[1].x = Round(x + j);\r
3030         arrow[1].y = Round(y - j*dx);\r
3031 \r
3032         if( d_x > s_x ) {\r
3033             x = (double) d_x - k;\r
3034             y = (double) d_y - k*dy;\r
3035         }\r
3036         else {\r
3037             x = (double) d_x + k;\r
3038             y = (double) d_y + k*dy;\r
3039         }\r
3040 \r
3041         arrow[2].x = Round(x + j);\r
3042         arrow[2].y = Round(y - j*dx);\r
3043 \r
3044         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
3045         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
3046 \r
3047         arrow[4].x = d_x;\r
3048         arrow[4].y = d_y;\r
3049 \r
3050         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
3051         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
3052 \r
3053         arrow[6].x = Round(x - j);\r
3054         arrow[6].y = Round(y + j*dx);\r
3055     }\r
3056 \r
3057     Polygon( hdc, arrow, 7 );\r
3058 }\r
3059 \r
3060 /* [AS] Draw an arrow between two squares */\r
3061 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3062 {\r
3063     int s_x, s_y, d_x, d_y;\r
3064     HPEN hpen;\r
3065     HPEN holdpen;\r
3066     HBRUSH hbrush;\r
3067     HBRUSH holdbrush;\r
3068     LOGBRUSH stLB;\r
3069 \r
3070     if( s_col == d_col && s_row == d_row ) {\r
3071         return;\r
3072     }\r
3073 \r
3074     /* Get source and destination points */\r
3075     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3076     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3077 \r
3078     if( d_y > s_y ) {\r
3079         d_y += squareSize / 4;\r
3080     }\r
3081     else if( d_y < s_y ) {\r
3082         d_y += 3 * squareSize / 4;\r
3083     }\r
3084     else {\r
3085         d_y += squareSize / 2;\r
3086     }\r
3087 \r
3088     if( d_x > s_x ) {\r
3089         d_x += squareSize / 4;\r
3090     }\r
3091     else if( d_x < s_x ) {\r
3092         d_x += 3 * squareSize / 4;\r
3093     }\r
3094     else {\r
3095         d_x += squareSize / 2;\r
3096     }\r
3097 \r
3098     s_x += squareSize / 2;\r
3099     s_y += squareSize / 2;\r
3100 \r
3101     /* Adjust width */\r
3102     A_WIDTH = squareSize / 14;\r
3103 \r
3104     /* Draw */\r
3105     stLB.lbStyle = BS_SOLID;\r
3106     stLB.lbColor = appData.highlightArrowColor;\r
3107     stLB.lbHatch = 0;\r
3108 \r
3109     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3110     holdpen = SelectObject( hdc, hpen );\r
3111     hbrush = CreateBrushIndirect( &stLB );\r
3112     holdbrush = SelectObject( hdc, hbrush );\r
3113 \r
3114     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3115 \r
3116     SelectObject( hdc, holdpen );\r
3117     SelectObject( hdc, holdbrush );\r
3118     DeleteObject( hpen );\r
3119     DeleteObject( hbrush );\r
3120 }\r
3121 \r
3122 BOOL HasHighlightInfo()\r
3123 {\r
3124     BOOL result = FALSE;\r
3125 \r
3126     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3127         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3128     {\r
3129         result = TRUE;\r
3130     }\r
3131 \r
3132     return result;\r
3133 }\r
3134 \r
3135 BOOL IsDrawArrowEnabled()\r
3136 {\r
3137     BOOL result = FALSE;\r
3138 \r
3139     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3140         result = TRUE;\r
3141     }\r
3142 \r
3143     return result;\r
3144 }\r
3145 \r
3146 VOID DrawArrowHighlight( HDC hdc )\r
3147 {\r
3148     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3149         DrawArrowBetweenSquares( hdc,\r
3150             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3151             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3152     }\r
3153 }\r
3154 \r
3155 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3156 {\r
3157     HRGN result = NULL;\r
3158 \r
3159     if( HasHighlightInfo() ) {\r
3160         int x1, y1, x2, y2;\r
3161         int sx, sy, dx, dy;\r
3162 \r
3163         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3164         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3165 \r
3166         sx = MIN( x1, x2 );\r
3167         sy = MIN( y1, y2 );\r
3168         dx = MAX( x1, x2 ) + squareSize;\r
3169         dy = MAX( y1, y2 ) + squareSize;\r
3170 \r
3171         result = CreateRectRgn( sx, sy, dx, dy );\r
3172     }\r
3173 \r
3174     return result;\r
3175 }\r
3176 \r
3177 /*\r
3178     Warning: this function modifies the behavior of several other functions. \r
3179     \r
3180     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3181     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3182     repaint is scattered all over the place, which is not good for features such as\r
3183     "arrow highlighting" that require a full repaint of the board.\r
3184 \r
3185     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3186     user interaction, when speed is not so important) but especially to avoid errors\r
3187     in the displayed graphics.\r
3188 \r
3189     In such patched places, I always try refer to this function so there is a single\r
3190     place to maintain knowledge.\r
3191     \r
3192     To restore the original behavior, just return FALSE unconditionally.\r
3193 */\r
3194 BOOL IsFullRepaintPreferrable()\r
3195 {\r
3196     BOOL result = FALSE;\r
3197 \r
3198     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3199         /* Arrow may appear on the board */\r
3200         result = TRUE;\r
3201     }\r
3202 \r
3203     return result;\r
3204 }\r
3205 \r
3206 /* \r
3207     This function is called by DrawPosition to know whether a full repaint must\r
3208     be forced or not.\r
3209 \r
3210     Only DrawPosition may directly call this function, which makes use of \r
3211     some state information. Other function should call DrawPosition specifying \r
3212     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3213 */\r
3214 BOOL DrawPositionNeedsFullRepaint()\r
3215 {\r
3216     BOOL result = FALSE;\r
3217 \r
3218     /* \r
3219         Probably a slightly better policy would be to trigger a full repaint\r
3220         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3221         but animation is fast enough that it's difficult to notice.\r
3222     */\r
3223     if( animInfo.piece == EmptySquare ) {\r
3224         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3225             result = TRUE;\r
3226         }\r
3227     }\r
3228 \r
3229     return result;\r
3230 }\r
3231 \r
3232 VOID\r
3233 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3234 {\r
3235   int row, column, x, y, square_color, piece_color;\r
3236   ChessSquare piece;\r
3237   HBRUSH oldBrush;\r
3238   HDC texture_hdc = NULL;\r
3239 \r
3240   /* [AS] Initialize background textures if needed */\r
3241   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3242       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3243       if( backTextureSquareSize != squareSize \r
3244        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3245           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3246           backTextureSquareSize = squareSize;\r
3247           RebuildTextureSquareInfo();\r
3248       }\r
3249 \r
3250       texture_hdc = CreateCompatibleDC( hdc );\r
3251   }\r
3252 \r
3253   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3254     for (column = 0; column < BOARD_WIDTH; column++) {\r
3255   \r
3256       SquareToPos(row, column, &x, &y);\r
3257 \r
3258       piece = board[row][column];\r
3259 \r
3260       square_color = ((column + row) % 2) == 1;\r
3261       if( gameInfo.variant == VariantXiangqi ) {\r
3262           square_color = !InPalace(row, column);\r
3263           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3264           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3265       }\r
3266       piece_color = (int) piece < (int) BlackPawn;\r
3267 \r
3268 \r
3269       /* [HGM] holdings file: light square or black */\r
3270       if(column == BOARD_LEFT-2) {\r
3271             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3272                 square_color = 1;\r
3273             else {\r
3274                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3275                 continue;\r
3276             }\r
3277       } else\r
3278       if(column == BOARD_RGHT + 1 ) {\r
3279             if( row < gameInfo.holdingsSize )\r
3280                 square_color = 1;\r
3281             else {\r
3282                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3283                 continue;\r
3284             }\r
3285       }\r
3286       if(column == BOARD_LEFT-1 ) /* left align */\r
3287             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3288       else if( column == BOARD_RGHT) /* right align */\r
3289             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3290       else\r
3291       if (appData.monoMode) {\r
3292         if (piece == EmptySquare) {\r
3293           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3294                  square_color ? WHITENESS : BLACKNESS);\r
3295         } else {\r
3296           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3297         }\r
3298       } \r
3299       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
3300           /* [AS] Draw the square using a texture bitmap */\r
3301           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3302           int r = row, c = column; // [HGM] do not flip board in flipView\r
3303           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3304 \r
3305           DrawTile( x, y, \r
3306               squareSize, squareSize, \r
3307               hdc, \r
3308               texture_hdc,\r
3309               backTextureSquareInfo[r][c].mode,\r
3310               backTextureSquareInfo[r][c].x,\r
3311               backTextureSquareInfo[r][c].y );\r
3312 \r
3313           SelectObject( texture_hdc, hbm );\r
3314 \r
3315           if (piece != EmptySquare) {\r
3316               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3317           }\r
3318       }\r
3319       else {\r
3320         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3321 \r
3322         oldBrush = SelectObject(hdc, brush );\r
3323         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3324         SelectObject(hdc, oldBrush);\r
3325         if (piece != EmptySquare)\r
3326           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3327       }\r
3328     }\r
3329   }\r
3330 \r
3331   if( texture_hdc != NULL ) {\r
3332     DeleteDC( texture_hdc );\r
3333   }\r
3334 }\r
3335 \r
3336 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3337 void fputDW(FILE *f, int x)\r
3338 {\r
3339         fputc(x     & 255, f);\r
3340         fputc(x>>8  & 255, f);\r
3341         fputc(x>>16 & 255, f);\r
3342         fputc(x>>24 & 255, f);\r
3343 }\r
3344 \r
3345 #define MAX_CLIPS 200   /* more than enough */\r
3346 \r
3347 VOID\r
3348 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3349 {\r
3350 //  HBITMAP bufferBitmap;\r
3351   BITMAP bi;\r
3352 //  RECT Rect;\r
3353   HDC tmphdc;\r
3354   HBITMAP hbm;\r
3355   int w = 100, h = 50;\r
3356 \r
3357   if(logo == NULL) return;\r
3358 //  GetClientRect(hwndMain, &Rect);\r
3359 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3360 //                                      Rect.bottom-Rect.top+1);\r
3361   tmphdc = CreateCompatibleDC(hdc);\r
3362   hbm = SelectObject(tmphdc, logo);\r
3363   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3364             w = bi.bmWidth;\r
3365             h = bi.bmHeight;\r
3366   }\r
3367   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3368                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3369   SelectObject(tmphdc, hbm);\r
3370   DeleteDC(tmphdc);\r
3371 }\r
3372 \r
3373 static HDC hdcSeek;\r
3374 \r
3375 // [HGM] seekgraph\r
3376 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3377 {\r
3378     POINT stPt;\r
3379     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3380     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3381     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3382     SelectObject( hdcSeek, hp );\r
3383 }\r
3384 \r
3385 // front-end wrapper for drawing functions to do rectangles\r
3386 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3387 {\r
3388     HPEN hp;\r
3389     RECT rc;\r
3390 \r
3391     if (hdcSeek == NULL) {\r
3392     hdcSeek = GetDC(hwndMain);\r
3393       if (!appData.monoMode) {\r
3394         SelectPalette(hdcSeek, hPal, FALSE);\r
3395         RealizePalette(hdcSeek);\r
3396       }\r
3397     }\r
3398     hp = SelectObject( hdcSeek, gridPen );\r
3399     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3400     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3401     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3402     SelectObject( hdcSeek, hp );\r
3403 }\r
3404 \r
3405 // front-end wrapper for putting text in graph\r
3406 void DrawSeekText(char *buf, int x, int y)\r
3407 {\r
3408         SIZE stSize;\r
3409         SetBkMode( hdcSeek, TRANSPARENT );\r
3410         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3411         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3412 }\r
3413 \r
3414 void DrawSeekDot(int x, int y, int color)\r
3415 {\r
3416         int square = color & 0x80;\r
3417         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3418                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3419         color &= 0x7F;\r
3420         if(square)\r
3421             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3422                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3423         else\r
3424             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3425                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3426             SelectObject(hdcSeek, oldBrush);\r
3427 }\r
3428 \r
3429 VOID\r
3430 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3431 {\r
3432   static Board lastReq[2], lastDrawn[2];\r
3433   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3434   static int lastDrawnFlipView = 0;\r
3435   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3436   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3437   HDC tmphdc;\r
3438   HDC hdcmem;\r
3439   HBITMAP bufferBitmap;\r
3440   HBITMAP oldBitmap;\r
3441   RECT Rect;\r
3442   HRGN clips[MAX_CLIPS];\r
3443   ChessSquare dragged_piece = EmptySquare;\r
3444   int nr = twoBoards*partnerUp;\r
3445 \r
3446   /* I'm undecided on this - this function figures out whether a full\r
3447    * repaint is necessary on its own, so there's no real reason to have the\r
3448    * caller tell it that.  I think this can safely be set to FALSE - but\r
3449    * if we trust the callers not to request full repaints unnessesarily, then\r
3450    * we could skip some clipping work.  In other words, only request a full\r
3451    * redraw when the majority of pieces have changed positions (ie. flip, \r
3452    * gamestart and similar)  --Hawk\r
3453    */\r
3454   Boolean fullrepaint = repaint;\r
3455 \r
3456   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3457 \r
3458   if( DrawPositionNeedsFullRepaint() ) {\r
3459       fullrepaint = TRUE;\r
3460   }\r
3461 \r
3462   if (board == NULL) {\r
3463     if (!lastReqValid[nr]) {\r
3464       return;\r
3465     }\r
3466     board = lastReq[nr];\r
3467   } else {\r
3468     CopyBoard(lastReq[nr], board);\r
3469     lastReqValid[nr] = 1;\r
3470   }\r
3471 \r
3472   if (doingSizing) {\r
3473     return;\r
3474   }\r
3475 \r
3476   if (IsIconic(hwndMain)) {\r
3477     return;\r
3478   }\r
3479 \r
3480   if (hdc == NULL) {\r
3481     hdc = GetDC(hwndMain);\r
3482     if (!appData.monoMode) {\r
3483       SelectPalette(hdc, hPal, FALSE);\r
3484       RealizePalette(hdc);\r
3485     }\r
3486     releaseDC = TRUE;\r
3487   } else {\r
3488     releaseDC = FALSE;\r
3489   }\r
3490 \r
3491   /* Create some work-DCs */\r
3492   hdcmem = CreateCompatibleDC(hdc);\r
3493   tmphdc = CreateCompatibleDC(hdc);\r
3494 \r
3495   /* If dragging is in progress, we temporarely remove the piece */\r
3496   /* [HGM] or temporarily decrease count if stacked              */\r
3497   /*       !! Moved to before board compare !!                   */\r
3498   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3499     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3500     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3501             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3502         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3503     } else \r
3504     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3505             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3506         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3507     } else \r
3508         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3509   }\r
3510 \r
3511   /* Figure out which squares need updating by comparing the \r
3512    * newest board with the last drawn board and checking if\r
3513    * flipping has changed.\r
3514    */\r
3515   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3516     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3517       for (column = 0; column < BOARD_WIDTH; column++) {\r
3518         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3519           SquareToPos(row, column, &x, &y);\r
3520           clips[num_clips++] =\r
3521             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3522         }\r
3523       }\r
3524     }\r
3525    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3526     for (i=0; i<2; i++) {\r
3527       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3528           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3529         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3530             lastDrawnHighlight.sq[i].y >= 0) {\r
3531           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3532                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3533           clips[num_clips++] =\r
3534             CreateRectRgn(x - lineGap, y - lineGap, \r
3535                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3536         }\r
3537         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3538           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3539           clips[num_clips++] =\r
3540             CreateRectRgn(x - lineGap, y - lineGap, \r
3541                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3542         }\r
3543       }\r
3544     }\r
3545     for (i=0; i<2; i++) {\r
3546       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3547           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3548         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3549             lastDrawnPremove.sq[i].y >= 0) {\r
3550           SquareToPos(lastDrawnPremove.sq[i].y,\r
3551                       lastDrawnPremove.sq[i].x, &x, &y);\r
3552           clips[num_clips++] =\r
3553             CreateRectRgn(x - lineGap, y - lineGap, \r
3554                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3555         }\r
3556         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3557             premoveHighlightInfo.sq[i].y >= 0) {\r
3558           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3559                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3560           clips[num_clips++] =\r
3561             CreateRectRgn(x - lineGap, y - lineGap, \r
3562                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3563         }\r
3564       }\r
3565     }\r
3566    } else { // nr == 1\r
3567         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3568         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3569         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3570         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3571       for (i=0; i<2; i++) {\r
3572         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3573             partnerHighlightInfo.sq[i].y >= 0) {\r
3574           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3575                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3576           clips[num_clips++] =\r
3577             CreateRectRgn(x - lineGap, y - lineGap, \r
3578                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3579         }\r
3580         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3581             oldPartnerHighlight.sq[i].y >= 0) {\r
3582           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3583                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3584           clips[num_clips++] =\r
3585             CreateRectRgn(x - lineGap, y - lineGap, \r
3586                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3587         }\r
3588       }\r
3589    }\r
3590   } else {\r
3591     fullrepaint = TRUE;\r
3592   }\r
3593 \r
3594   /* Create a buffer bitmap - this is the actual bitmap\r
3595    * being written to.  When all the work is done, we can\r
3596    * copy it to the real DC (the screen).  This avoids\r
3597    * the problems with flickering.\r
3598    */\r
3599   GetClientRect(hwndMain, &Rect);\r
3600   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3601                                         Rect.bottom-Rect.top+1);\r
3602   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3603   if (!appData.monoMode) {\r
3604     SelectPalette(hdcmem, hPal, FALSE);\r
3605   }\r
3606 \r
3607   /* Create clips for dragging */\r
3608   if (!fullrepaint) {\r
3609     if (dragInfo.from.x >= 0) {\r
3610       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3611       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3612     }\r
3613     if (dragInfo.start.x >= 0) {\r
3614       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3615       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3616     }\r
3617     if (dragInfo.pos.x >= 0) {\r
3618       x = dragInfo.pos.x - squareSize / 2;\r
3619       y = dragInfo.pos.y - squareSize / 2;\r
3620       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3621     }\r
3622     if (dragInfo.lastpos.x >= 0) {\r
3623       x = dragInfo.lastpos.x - squareSize / 2;\r
3624       y = dragInfo.lastpos.y - squareSize / 2;\r
3625       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3626     }\r
3627   }\r
3628 \r
3629   /* Are we animating a move?  \r
3630    * If so, \r
3631    *   - remove the piece from the board (temporarely)\r
3632    *   - calculate the clipping region\r
3633    */\r
3634   if (!fullrepaint) {\r
3635     if (animInfo.piece != EmptySquare) {\r
3636       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3637       x = boardRect.left + animInfo.lastpos.x;\r
3638       y = boardRect.top + animInfo.lastpos.y;\r
3639       x2 = boardRect.left + animInfo.pos.x;\r
3640       y2 = boardRect.top + animInfo.pos.y;\r
3641       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3642       /* Slight kludge.  The real problem is that after AnimateMove is\r
3643          done, the position on the screen does not match lastDrawn.\r
3644          This currently causes trouble only on e.p. captures in\r
3645          atomic, where the piece moves to an empty square and then\r
3646          explodes.  The old and new positions both had an empty square\r
3647          at the destination, but animation has drawn a piece there and\r
3648          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3649       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3650     }\r
3651   }\r
3652 \r
3653   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3654   if (num_clips == 0)\r
3655     fullrepaint = TRUE;\r
3656 \r
3657   /* Set clipping on the memory DC */\r
3658   if (!fullrepaint) {\r
3659     SelectClipRgn(hdcmem, clips[0]);\r
3660     for (x = 1; x < num_clips; x++) {\r
3661       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3662         abort();  // this should never ever happen!\r
3663     }\r
3664   }\r
3665 \r
3666   /* Do all the drawing to the memory DC */\r
3667   if(explodeInfo.radius) { // [HGM] atomic\r
3668         HBRUSH oldBrush;\r
3669         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3670         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3671         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3672         x += squareSize/2;\r
3673         y += squareSize/2;\r
3674         if(!fullrepaint) {\r
3675           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3676           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3677         }\r
3678         DrawGridOnDC(hdcmem);\r
3679         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3680         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3681         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3682         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3683         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3684         SelectObject(hdcmem, oldBrush);\r
3685   } else {\r
3686     DrawGridOnDC(hdcmem);\r
3687     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3688         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3689         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3690     } else {\r
3691         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3692         oldPartnerHighlight = partnerHighlightInfo;\r
3693     }\r
3694     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3695   }\r
3696   if(nr == 0) // [HGM] dual: markers only on left board\r
3697   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3698     for (column = 0; column < BOARD_WIDTH; column++) {\r
3699         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3700             HBRUSH oldBrush = SelectObject(hdcmem, \r
3701                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3702             SquareToPos(row, column, &x, &y);\r
3703             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3704                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3705             SelectObject(hdcmem, oldBrush);\r
3706         }\r
3707     }\r
3708   }\r
3709   if(logoHeight) {\r
3710         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3711         if(appData.autoLogo) {\r
3712           \r
3713           switch(gameMode) { // pick logos based on game mode\r
3714             case IcsObserving:\r
3715                 whiteLogo = second.programLogo; // ICS logo\r
3716                 blackLogo = second.programLogo;\r
3717             default:\r
3718                 break;\r
3719             case IcsPlayingWhite:\r
3720                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3721                 blackLogo = second.programLogo; // ICS logo\r
3722                 break;\r
3723             case IcsPlayingBlack:\r
3724                 whiteLogo = second.programLogo; // ICS logo\r
3725                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3726                 break;\r
3727             case TwoMachinesPlay:\r
3728                 if(first.twoMachinesColor[0] == 'b') {\r
3729                     whiteLogo = second.programLogo;\r
3730                     blackLogo = first.programLogo;\r
3731                 }\r
3732                 break;\r
3733             case MachinePlaysWhite:\r
3734                 blackLogo = userLogo;\r
3735                 break;\r
3736             case MachinePlaysBlack:\r
3737                 whiteLogo = userLogo;\r
3738                 blackLogo = first.programLogo;\r
3739           }\r
3740         }\r
3741         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3742         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3743   }\r
3744 \r
3745   if( appData.highlightMoveWithArrow ) {\r
3746     DrawArrowHighlight(hdcmem);\r
3747   }\r
3748 \r
3749   DrawCoordsOnDC(hdcmem);\r
3750 \r
3751   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3752                  /* to make sure lastDrawn contains what is actually drawn */\r
3753 \r
3754   /* Put the dragged piece back into place and draw it (out of place!) */\r
3755     if (dragged_piece != EmptySquare) {\r
3756     /* [HGM] or restack */\r
3757     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3758                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3759     else\r
3760     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3761                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3762     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3763     x = dragInfo.pos.x - squareSize / 2;\r
3764     y = dragInfo.pos.y - squareSize / 2;\r
3765     DrawPieceOnDC(hdcmem, dragged_piece,\r
3766                   ((int) dragged_piece < (int) BlackPawn), \r
3767                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3768   }   \r
3769   \r
3770   /* Put the animated piece back into place and draw it */\r
3771   if (animInfo.piece != EmptySquare) {\r
3772     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3773     x = boardRect.left + animInfo.pos.x;\r
3774     y = boardRect.top + animInfo.pos.y;\r
3775     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3776                   ((int) animInfo.piece < (int) BlackPawn),\r
3777                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3778   }\r
3779 \r
3780   /* Release the bufferBitmap by selecting in the old bitmap \r
3781    * and delete the memory DC\r
3782    */\r
3783   SelectObject(hdcmem, oldBitmap);\r
3784   DeleteDC(hdcmem);\r
3785 \r
3786   /* Set clipping on the target DC */\r
3787   if (!fullrepaint) {\r
3788     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3789         RECT rect;\r
3790         GetRgnBox(clips[x], &rect);\r
3791         DeleteObject(clips[x]);\r
3792         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3793                           rect.right + wpMain.width/2, rect.bottom);\r
3794     }\r
3795     SelectClipRgn(hdc, clips[0]);\r
3796     for (x = 1; x < num_clips; x++) {\r
3797       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3798         abort();   // this should never ever happen!\r
3799     } \r
3800   }\r
3801 \r
3802   /* Copy the new bitmap onto the screen in one go.\r
3803    * This way we avoid any flickering\r
3804    */\r
3805   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3806   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3807          boardRect.right - boardRect.left,\r
3808          boardRect.bottom - boardRect.top,\r
3809          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3810   if(saveDiagFlag) { \r
3811     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
3812     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3813 \r
3814     GetObject(bufferBitmap, sizeof(b), &b);\r
3815     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
3816         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3817         bih.biWidth = b.bmWidth;\r
3818         bih.biHeight = b.bmHeight;\r
3819         bih.biPlanes = 1;\r
3820         bih.biBitCount = b.bmBitsPixel;\r
3821         bih.biCompression = 0;\r
3822         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3823         bih.biXPelsPerMeter = 0;\r
3824         bih.biYPelsPerMeter = 0;\r
3825         bih.biClrUsed = 0;\r
3826         bih.biClrImportant = 0;\r
3827 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3828 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3829         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3830 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3831 \r
3832         wb = b.bmWidthBytes;\r
3833         // count colors\r
3834         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3835                 int k = ((int*) pData)[i];\r
3836                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3837                 if(j >= 16) break;\r
3838                 color[j] = k;\r
3839                 if(j >= nrColors) nrColors = j+1;\r
3840         }\r
3841         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3842                 INT p = 0;\r
3843                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3844                     for(w=0; w<(wb>>2); w+=2) {\r
3845                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3846                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3847                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3848                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3849                         pData[p++] = m | j<<4;\r
3850                     }\r
3851                     while(p&3) pData[p++] = 0;\r
3852                 }\r
3853                 fac = 3;\r
3854                 wb = ((wb+31)>>5)<<2;\r
3855         }\r
3856         // write BITMAPFILEHEADER\r
3857         fprintf(diagFile, "BM");\r
3858         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3859         fputDW(diagFile, 0);\r
3860         fputDW(diagFile, 0x36 + (fac?64:0));\r
3861         // write BITMAPINFOHEADER\r
3862         fputDW(diagFile, 40);\r
3863         fputDW(diagFile, b.bmWidth);\r
3864         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3865         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3866         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3867         fputDW(diagFile, 0);\r
3868         fputDW(diagFile, 0);\r
3869         fputDW(diagFile, 0);\r
3870         fputDW(diagFile, 0);\r
3871         fputDW(diagFile, 0);\r
3872         fputDW(diagFile, 0);\r
3873         // write color table\r
3874         if(fac)\r
3875         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3876         // write bitmap data\r
3877         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3878                 fputc(pData[i], diagFile);\r
3879      }\r
3880   }\r
3881 \r
3882   SelectObject(tmphdc, oldBitmap);\r
3883 \r
3884   /* Massive cleanup */\r
3885   for (x = 0; x < num_clips; x++)\r
3886     DeleteObject(clips[x]);\r
3887 \r
3888   DeleteDC(tmphdc);\r
3889   DeleteObject(bufferBitmap);\r
3890 \r
3891   if (releaseDC) \r
3892     ReleaseDC(hwndMain, hdc);\r
3893   \r
3894   if (lastDrawnFlipView != flipView && nr == 0) {\r
3895     if (flipView)\r
3896       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3897     else\r
3898       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3899   }\r
3900 \r
3901 /*  CopyBoard(lastDrawn, board);*/\r
3902   lastDrawnHighlight = highlightInfo;\r
3903   lastDrawnPremove   = premoveHighlightInfo;\r
3904   lastDrawnFlipView = flipView;\r
3905   lastDrawnValid[nr] = 1;\r
3906 }\r
3907 \r
3908 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3909 int\r
3910 SaveDiagram(f)\r
3911      FILE *f;\r
3912 {\r
3913     saveDiagFlag = 1; diagFile = f;\r
3914     HDCDrawPosition(NULL, TRUE, NULL);\r
3915 \r
3916     saveDiagFlag = 0;\r
3917 \r
3918 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
3919     \r
3920     fclose(f);\r
3921     return TRUE;\r
3922 }\r
3923 \r
3924 \r
3925 /*---------------------------------------------------------------------------*\\r
3926 | CLIENT PAINT PROCEDURE\r
3927 |   This is the main event-handler for the WM_PAINT message.\r
3928 |\r
3929 \*---------------------------------------------------------------------------*/\r
3930 VOID\r
3931 PaintProc(HWND hwnd)\r
3932 {\r
3933   HDC         hdc;\r
3934   PAINTSTRUCT ps;\r
3935   HFONT       oldFont;\r
3936 \r
3937   if((hdc = BeginPaint(hwnd, &ps))) {\r
3938     if (IsIconic(hwnd)) {\r
3939       DrawIcon(hdc, 2, 2, iconCurrent);\r
3940     } else {\r
3941       if (!appData.monoMode) {\r
3942         SelectPalette(hdc, hPal, FALSE);\r
3943         RealizePalette(hdc);\r
3944       }\r
3945       HDCDrawPosition(hdc, 1, NULL);\r
3946       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
3947         flipView = !flipView; partnerUp = !partnerUp;\r
3948         HDCDrawPosition(hdc, 1, NULL);\r
3949         flipView = !flipView; partnerUp = !partnerUp;\r
3950       }\r
3951       oldFont =\r
3952         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3953       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
3954                  ETO_CLIPPED|ETO_OPAQUE,\r
3955                  &messageRect, messageText, strlen(messageText), NULL);\r
3956       SelectObject(hdc, oldFont);\r
3957       DisplayBothClocks();\r
3958     }\r
3959     EndPaint(hwnd,&ps);\r
3960   }\r
3961 \r
3962   return;\r
3963 }\r
3964 \r
3965 \r
3966 /*\r
3967  * If the user selects on a border boundary, return -1; if off the board,\r
3968  *   return -2.  Otherwise map the event coordinate to the square.\r
3969  * The offset boardRect.left or boardRect.top must already have been\r
3970  *   subtracted from x.\r
3971  */\r
3972 int EventToSquare(x, limit)\r
3973      int x, limit;\r
3974 {\r
3975   if (x <= 0)\r
3976     return -2;\r
3977   if (x < lineGap)\r
3978     return -1;\r
3979   x -= lineGap;\r
3980   if ((x % (squareSize + lineGap)) >= squareSize)\r
3981     return -1;\r
3982   x /= (squareSize + lineGap);\r
3983     if (x >= limit)\r
3984     return -2;\r
3985   return x;\r
3986 }\r
3987 \r
3988 typedef struct {\r
3989   char piece;\r
3990   int command;\r
3991   char* name;\r
3992 } DropEnable;\r
3993 \r
3994 DropEnable dropEnables[] = {\r
3995   { 'P', DP_Pawn, N_("Pawn") },\r
3996   { 'N', DP_Knight, N_("Knight") },\r
3997   { 'B', DP_Bishop, N_("Bishop") },\r
3998   { 'R', DP_Rook, N_("Rook") },\r
3999   { 'Q', DP_Queen, N_("Queen") },\r
4000 };\r
4001 \r
4002 VOID\r
4003 SetupDropMenu(HMENU hmenu)\r
4004 {\r
4005   int i, count, enable;\r
4006   char *p;\r
4007   extern char white_holding[], black_holding[];\r
4008   char item[MSG_SIZ];\r
4009 \r
4010   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4011     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4012                dropEnables[i].piece);\r
4013     count = 0;\r
4014     while (p && *p++ == dropEnables[i].piece) count++;\r
4015     sprintf(item, "%s  %d", T_(dropEnables[i].name), count);\r
4016     enable = count > 0 || !appData.testLegality\r
4017       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4018                       && !appData.icsActive);\r
4019     ModifyMenu(hmenu, dropEnables[i].command,\r
4020                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4021                dropEnables[i].command, item);\r
4022   }\r
4023 }\r
4024 \r
4025 void DragPieceBegin(int x, int y)\r
4026 {\r
4027       dragInfo.lastpos.x = boardRect.left + x;\r
4028       dragInfo.lastpos.y = boardRect.top + y;\r
4029       dragInfo.from.x = fromX;\r
4030       dragInfo.from.y = fromY;\r
4031       dragInfo.start = dragInfo.from;\r
4032       SetCapture(hwndMain);\r
4033 }\r
4034 \r
4035 void DragPieceEnd(int x, int y)\r
4036 {\r
4037     ReleaseCapture();\r
4038     dragInfo.start.x = dragInfo.start.y = -1;\r
4039     dragInfo.from = dragInfo.start;\r
4040     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4041 }\r
4042 \r
4043 /* Event handler for mouse messages */\r
4044 VOID\r
4045 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4046 {\r
4047   int x, y, menuNr;\r
4048   POINT pt;\r
4049   static int recursive = 0;\r
4050   HMENU hmenu;\r
4051   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4052 \r
4053   if (recursive) {\r
4054     if (message == WM_MBUTTONUP) {\r
4055       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4056          to the middle button: we simulate pressing the left button too!\r
4057          */\r
4058       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4059       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4060     }\r
4061     return;\r
4062   }\r
4063   recursive++;\r
4064   \r
4065   pt.x = LOWORD(lParam);\r
4066   pt.y = HIWORD(lParam);\r
4067   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4068   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4069   if (!flipView && y >= 0) {\r
4070     y = BOARD_HEIGHT - 1 - y;\r
4071   }\r
4072   if (flipView && x >= 0) {\r
4073     x = BOARD_WIDTH - 1 - x;\r
4074   }\r
4075 \r
4076   switch (message) {\r
4077   case WM_LBUTTONDOWN:\r
4078       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4079         if (gameMode == EditPosition) {\r
4080           SetWhiteToPlayEvent();\r
4081         } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {\r
4082           AdjustClock(flipClock, -1);\r
4083         } else if (gameMode == IcsPlayingBlack ||\r
4084                    gameMode == MachinePlaysWhite) {\r
4085           CallFlagEvent();\r
4086         }\r
4087       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4088         if (gameMode == EditPosition) {\r
4089           SetBlackToPlayEvent();\r
4090         } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {\r
4091           AdjustClock(!flipClock, -1);\r
4092         } else if (gameMode == IcsPlayingWhite ||\r
4093                    gameMode == MachinePlaysBlack) {\r
4094           CallFlagEvent();\r
4095         }\r
4096       }\r
4097       dragInfo.start.x = dragInfo.start.y = -1;\r
4098       dragInfo.from = dragInfo.start;\r
4099     if(fromX == -1 && frozen) { // not sure where this is for\r
4100                 fromX = fromY = -1; \r
4101       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4102       break;\r
4103     }\r
4104       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4105       DrawPosition(TRUE, NULL);\r
4106     break;\r
4107 \r
4108   case WM_LBUTTONUP:\r
4109       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4110       DrawPosition(TRUE, NULL);\r
4111     break;\r
4112 \r
4113   case WM_MOUSEMOVE:\r
4114     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4115     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4116     if ((appData.animateDragging || appData.highlightDragging)\r
4117         && (wParam & MK_LBUTTON)\r
4118         && dragInfo.from.x >= 0) \r
4119     {\r
4120       BOOL full_repaint = FALSE;\r
4121 \r
4122       if (appData.animateDragging) {\r
4123         dragInfo.pos = pt;\r
4124       }\r
4125       if (appData.highlightDragging) {\r
4126         SetHighlights(fromX, fromY, x, y);\r
4127         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4128             full_repaint = TRUE;\r
4129         }\r
4130       }\r
4131       \r
4132       DrawPosition( full_repaint, NULL);\r
4133       \r
4134       dragInfo.lastpos = dragInfo.pos;\r
4135     }\r
4136     break;\r
4137 \r
4138   case WM_MOUSEWHEEL: // [DM]\r
4139     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4140        /* Mouse Wheel is being rolled forward\r
4141         * Play moves forward\r
4142         */\r
4143        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4144                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4145        /* Mouse Wheel is being rolled backward\r
4146         * Play moves backward\r
4147         */\r
4148        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4149                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4150     }\r
4151     break;\r
4152 \r
4153   case WM_MBUTTONUP:\r
4154   case WM_RBUTTONUP:\r
4155     ReleaseCapture();\r
4156     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4157     break;\r
4158  \r
4159   case WM_MBUTTONDOWN:\r
4160   case WM_RBUTTONDOWN:\r
4161     ErrorPopDown();\r
4162     ReleaseCapture();\r
4163     fromX = fromY = -1;\r
4164     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4165     dragInfo.start.x = dragInfo.start.y = -1;\r
4166     dragInfo.from = dragInfo.start;\r
4167     dragInfo.lastpos = dragInfo.pos;\r
4168     if (appData.highlightDragging) {\r
4169       ClearHighlights();\r
4170     }\r
4171     if(y == -2) {\r
4172       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4173       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4174           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4175       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4176           if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4177       }\r
4178       break;\r
4179     }\r
4180     DrawPosition(TRUE, NULL);\r
4181 \r
4182     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4183     switch (menuNr) {\r
4184     case 0:\r
4185       if (message == WM_MBUTTONDOWN) {\r
4186         buttonCount = 3;  /* even if system didn't think so */\r
4187         if (wParam & MK_SHIFT) \r
4188           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4189         else\r
4190           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4191       } else { /* message == WM_RBUTTONDOWN */\r
4192         /* Just have one menu, on the right button.  Windows users don't\r
4193            think to try the middle one, and sometimes other software steals\r
4194            it, or it doesn't really exist. */\r
4195         if(gameInfo.variant != VariantShogi)\r
4196             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4197         else\r
4198             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4199       }\r
4200       break;\r
4201     case 2:\r
4202       SetCapture(hwndMain);
4203       break;\r
4204     case 1:\r
4205       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4206       SetupDropMenu(hmenu);\r
4207       MenuPopup(hwnd, pt, hmenu, -1);\r
4208     default:\r
4209       break;\r
4210     }\r
4211     break;\r
4212   }\r
4213 \r
4214   recursive--;\r
4215 }\r
4216 \r
4217 /* Preprocess messages for buttons in main window */\r
4218 LRESULT CALLBACK\r
4219 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4220 {\r
4221   int id = GetWindowLong(hwnd, GWL_ID);\r
4222   int i, dir;\r
4223 \r
4224   for (i=0; i<N_BUTTONS; i++) {\r
4225     if (buttonDesc[i].id == id) break;\r
4226   }\r
4227   if (i == N_BUTTONS) return 0;\r
4228   switch (message) {\r
4229   case WM_KEYDOWN:\r
4230     switch (wParam) {\r
4231     case VK_LEFT:\r
4232     case VK_RIGHT:\r
4233       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4234       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4235       return TRUE;\r
4236     }\r
4237     break;\r
4238   case WM_CHAR:\r
4239     switch (wParam) {\r
4240     case '\r':\r
4241       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4242       return TRUE;\r
4243     default:\r
4244       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4245         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4246         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4247         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4248         SetFocus(h);\r
4249         SendMessage(h, WM_CHAR, wParam, lParam);\r
4250         return TRUE;\r
4251       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4252         PopUpMoveDialog((char)wParam);\r
4253       }\r
4254       break;\r
4255     }\r
4256     break;\r
4257   }\r
4258   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4259 }\r
4260 \r
4261 /* Process messages for Promotion dialog box */\r
4262 LRESULT CALLBACK\r
4263 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4264 {\r
4265   char promoChar;\r
4266 \r
4267   switch (message) {\r
4268   case WM_INITDIALOG: /* message: initialize dialog box */\r
4269     /* Center the dialog over the application window */\r
4270     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4271     Translate(hDlg, DLG_PromotionKing);\r
4272     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4273       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4274        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4275                SW_SHOW : SW_HIDE);\r
4276     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4277     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4278        ((PieceToChar(WhiteAngel) >= 'A' &&\r
4279          PieceToChar(WhiteAngel) != '~') ||\r
4280         (PieceToChar(BlackAngel) >= 'A' &&\r
4281          PieceToChar(BlackAngel) != '~')   ) ?\r
4282                SW_SHOW : SW_HIDE);\r
4283     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4284        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
4285          PieceToChar(WhiteMarshall) != '~') ||\r
4286         (PieceToChar(BlackMarshall) >= 'A' &&\r
4287          PieceToChar(BlackMarshall) != '~')   ) ?\r
4288                SW_SHOW : SW_HIDE);\r
4289     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4290     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4291        gameInfo.variant != VariantShogi ?\r
4292                SW_SHOW : SW_HIDE);\r
4293     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4294        gameInfo.variant != VariantShogi ?\r
4295                SW_SHOW : SW_HIDE);\r
4296     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
4297        gameInfo.variant == VariantShogi ?\r
4298                SW_SHOW : SW_HIDE);\r
4299     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
4300        gameInfo.variant == VariantShogi ?\r
4301                SW_SHOW : SW_HIDE);\r
4302     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4303        gameInfo.variant == VariantSuper ?\r
4304                SW_SHOW : SW_HIDE);\r
4305     return TRUE;\r
4306 \r
4307   case WM_COMMAND: /* message: received a command */\r
4308     switch (LOWORD(wParam)) {\r
4309     case IDCANCEL:\r
4310       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4311       ClearHighlights();\r
4312       DrawPosition(FALSE, NULL);\r
4313       return TRUE;\r
4314     case PB_King:\r
4315       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4316       break;\r
4317     case PB_Queen:\r
4318       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
4319       break;\r
4320     case PB_Rook:\r
4321       promoChar = PieceToChar(BlackRook);\r
4322       break;\r
4323     case PB_Bishop:\r
4324       promoChar = PieceToChar(BlackBishop);\r
4325       break;\r
4326     case PB_Chancellor:\r
4327       promoChar = PieceToChar(BlackMarshall);\r
4328       break;\r
4329     case PB_Archbishop:\r
4330       promoChar = PieceToChar(BlackAngel);\r
4331       break;\r
4332     case PB_Knight:\r
4333       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
4334       break;\r
4335     default:\r
4336       return FALSE;\r
4337     }\r
4338     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4339     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
4340        only show the popup when we are already sure the move is valid or\r
4341        legal. We pass a faulty move type, but the kludge is that FinishMove\r
4342        will figure out it is a promotion from the promoChar. */\r
4343     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4344     fromX = fromY = -1;\r
4345     if (!appData.highlightLastMove) {\r
4346       ClearHighlights();\r
4347       DrawPosition(FALSE, NULL);\r
4348     }\r
4349     return TRUE;\r
4350   }\r
4351   return FALSE;\r
4352 }\r
4353 \r
4354 /* Pop up promotion dialog */\r
4355 VOID\r
4356 PromotionPopup(HWND hwnd)\r
4357 {\r
4358   FARPROC lpProc;\r
4359 \r
4360   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4361   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4362     hwnd, (DLGPROC)lpProc);\r
4363   FreeProcInstance(lpProc);\r
4364 }\r
4365 \r
4366 void\r
4367 PromotionPopUp()\r
4368 {\r
4369   DrawPosition(TRUE, NULL);\r
4370   PromotionPopup(hwndMain);\r
4371 }\r
4372 \r
4373 /* Toggle ShowThinking */\r
4374 VOID\r
4375 ToggleShowThinking()\r
4376 {\r
4377   appData.showThinking = !appData.showThinking;\r
4378   ShowThinkingEvent();\r
4379 }\r
4380 \r
4381 VOID\r
4382 LoadGameDialog(HWND hwnd, char* title)\r
4383 {\r
4384   UINT number = 0;\r
4385   FILE *f;\r
4386   char fileTitle[MSG_SIZ];\r
4387   f = OpenFileDialog(hwnd, "rb", "",\r
4388                      appData.oldSaveStyle ? "gam" : "pgn",\r
4389                      GAME_FILT,\r
4390                      title, &number, fileTitle, NULL);\r
4391   if (f != NULL) {\r
4392     cmailMsgLoaded = FALSE;\r
4393     if (number == 0) {\r
4394       int error = GameListBuild(f);\r
4395       if (error) {\r
4396         DisplayError(_("Cannot build game list"), error);\r
4397       } else if (!ListEmpty(&gameList) &&\r
4398                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4399         GameListPopUp(f, fileTitle);\r
4400         return;\r
4401       }\r
4402       GameListDestroy();\r
4403       number = 1;\r
4404     }\r
4405     LoadGame(f, number, fileTitle, FALSE);\r
4406   }\r
4407 }\r
4408 \r
4409 int get_term_width()\r
4410 {\r
4411     HDC hdc;\r
4412     TEXTMETRIC tm;\r
4413     RECT rc;\r
4414     HFONT hfont, hold_font;\r
4415     LOGFONT lf;\r
4416     HWND hText;\r
4417 \r
4418     if (hwndConsole)\r
4419         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4420     else\r
4421         return 79;\r
4422 \r
4423     // get the text metrics\r
4424     hdc = GetDC(hText);\r
4425     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4426     if (consoleCF.dwEffects & CFE_BOLD)\r
4427         lf.lfWeight = FW_BOLD;\r
4428     if (consoleCF.dwEffects & CFE_ITALIC)\r
4429         lf.lfItalic = TRUE;\r
4430     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4431         lf.lfStrikeOut = TRUE;\r
4432     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4433         lf.lfUnderline = TRUE;\r
4434     hfont = CreateFontIndirect(&lf);\r
4435     hold_font = SelectObject(hdc, hfont);\r
4436     GetTextMetrics(hdc, &tm);\r
4437     SelectObject(hdc, hold_font);\r
4438     DeleteObject(hfont);\r
4439     ReleaseDC(hText, hdc);\r
4440 \r
4441     // get the rectangle\r
4442     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4443 \r
4444     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4445 }\r
4446 \r
4447 void UpdateICSWidth(HWND hText)\r
4448 {\r
4449     LONG old_width, new_width;\r
4450 \r
4451     new_width = get_term_width(hText, FALSE);\r
4452     old_width = GetWindowLong(hText, GWL_USERDATA);\r
4453     if (new_width != old_width)\r
4454     {\r
4455         ics_update_width(new_width);\r
4456         SetWindowLong(hText, GWL_USERDATA, new_width);\r
4457     }\r
4458 }\r
4459 \r
4460 VOID\r
4461 ChangedConsoleFont()\r
4462 {\r
4463   CHARFORMAT cfmt;\r
4464   CHARRANGE tmpsel, sel;\r
4465   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4466   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4467   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4468   PARAFORMAT paraf;\r
4469 \r
4470   cfmt.cbSize = sizeof(CHARFORMAT);\r
4471   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4472     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4473                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4474   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4475    * size.  This was undocumented in the version of MSVC++ that I had\r
4476    * when I wrote the code, but is apparently documented now.\r
4477    */\r
4478   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4479   cfmt.bCharSet = f->lf.lfCharSet;\r
4480   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4481   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4482   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4483   /* Why are the following seemingly needed too? */\r
4484   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4485   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4486   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4487   tmpsel.cpMin = 0;\r
4488   tmpsel.cpMax = -1; /*999999?*/\r
4489   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4490   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4491   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4492    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4493    */\r
4494   paraf.cbSize = sizeof(paraf);\r
4495   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4496   paraf.dxStartIndent = 0;\r
4497   paraf.dxOffset = WRAP_INDENT;\r
4498   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4499   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4500   UpdateICSWidth(hText);\r
4501 }\r
4502 \r
4503 /*---------------------------------------------------------------------------*\\r
4504  *\r
4505  * Window Proc for main window\r
4506  *\r
4507 \*---------------------------------------------------------------------------*/\r
4508 \r
4509 /* Process messages for main window, etc. */\r
4510 LRESULT CALLBACK\r
4511 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4512 {\r
4513   FARPROC lpProc;\r
4514   int wmId, wmEvent;\r
4515   char *defName;\r
4516   FILE *f;\r
4517   UINT number;\r
4518   char fileTitle[MSG_SIZ];\r
4519   char buf[MSG_SIZ];\r
4520   static SnapData sd;\r
4521 \r
4522   switch (message) {\r
4523 \r
4524   case WM_PAINT: /* message: repaint portion of window */\r
4525     PaintProc(hwnd);\r
4526     break;\r
4527 \r
4528   case WM_ERASEBKGND:\r
4529     if (IsIconic(hwnd)) {\r
4530       /* Cheat; change the message */\r
4531       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4532     } else {\r
4533       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4534     }\r
4535     break;\r
4536 \r
4537   case WM_LBUTTONDOWN:\r
4538   case WM_MBUTTONDOWN:\r
4539   case WM_RBUTTONDOWN:\r
4540   case WM_LBUTTONUP:\r
4541   case WM_MBUTTONUP:\r
4542   case WM_RBUTTONUP:\r
4543   case WM_MOUSEMOVE:\r
4544   case WM_MOUSEWHEEL:\r
4545     MouseEvent(hwnd, message, wParam, lParam);\r
4546     break;\r
4547 \r
4548   JAWS_KB_NAVIGATION\r
4549 \r
4550   case WM_CHAR:\r
4551     \r
4552     JAWS_ALT_INTERCEPT\r
4553 \r
4554     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4555         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4556         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4557         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4558         SetFocus(h);\r
4559         SendMessage(h, message, wParam, lParam);\r
4560     } else if(lParam != KF_REPEAT) {\r
4561         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4562                 PopUpMoveDialog((char)wParam);\r
4563         } else if((char)wParam == 003) CopyGameToClipboard();\r
4564          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4565     }\r
4566 \r
4567     break;\r
4568 \r
4569   case WM_PALETTECHANGED:\r
4570     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4571       int nnew;\r
4572       HDC hdc = GetDC(hwndMain);\r
4573       SelectPalette(hdc, hPal, TRUE);\r
4574       nnew = RealizePalette(hdc);\r
4575       if (nnew > 0) {\r
4576         paletteChanged = TRUE;\r
4577         InvalidateRect(hwnd, &boardRect, FALSE);\r
4578       }\r
4579       ReleaseDC(hwnd, hdc);\r
4580     }\r
4581     break;\r
4582 \r
4583   case WM_QUERYNEWPALETTE:\r
4584     if (!appData.monoMode /*&& paletteChanged*/) {\r
4585       int nnew;\r
4586       HDC hdc = GetDC(hwndMain);\r
4587       paletteChanged = FALSE;\r
4588       SelectPalette(hdc, hPal, FALSE);\r
4589       nnew = RealizePalette(hdc);\r
4590       if (nnew > 0) {\r
4591         InvalidateRect(hwnd, &boardRect, FALSE);\r
4592       }\r
4593       ReleaseDC(hwnd, hdc);\r
4594       return TRUE;\r
4595     }\r
4596     return FALSE;\r
4597 \r
4598   case WM_COMMAND: /* message: command from application menu */\r
4599     wmId    = LOWORD(wParam);\r
4600     wmEvent = HIWORD(wParam);\r
4601 \r
4602     switch (wmId) {\r
4603     case IDM_NewGame:\r
4604       ResetGameEvent();\r
4605       SAY("new game enter a move to play against the computer with white");\r
4606       break;\r
4607 \r
4608     case IDM_NewGameFRC:\r
4609       if( NewGameFRC() == 0 ) {\r
4610         ResetGameEvent();\r
4611       }\r
4612       break;\r
4613 \r
4614     case IDM_NewVariant:\r
4615       NewVariantPopup(hwnd);\r
4616       break;\r
4617 \r
4618     case IDM_LoadGame:\r
4619       LoadGameDialog(hwnd, _("Load Game from File"));\r
4620       break;\r
4621 \r
4622     case IDM_LoadNextGame:\r
4623       ReloadGame(1);\r
4624       break;\r
4625 \r
4626     case IDM_LoadPrevGame:\r
4627       ReloadGame(-1);\r
4628       break;\r
4629 \r
4630     case IDM_ReloadGame:\r
4631       ReloadGame(0);\r
4632       break;\r
4633 \r
4634     case IDM_LoadPosition:\r
4635       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4636         Reset(FALSE, TRUE);\r
4637       }\r
4638       number = 1;\r
4639       f = OpenFileDialog(hwnd, "rb", "",\r
4640                          appData.oldSaveStyle ? "pos" : "fen",\r
4641                          POSITION_FILT,\r
4642                          _("Load Position from File"), &number, fileTitle, NULL);\r
4643       if (f != NULL) {\r
4644         LoadPosition(f, number, fileTitle);\r
4645       }\r
4646       break;\r
4647 \r
4648     case IDM_LoadNextPosition:\r
4649       ReloadPosition(1);\r
4650       break;\r
4651 \r
4652     case IDM_LoadPrevPosition:\r
4653       ReloadPosition(-1);\r
4654       break;\r
4655 \r
4656     case IDM_ReloadPosition:\r
4657       ReloadPosition(0);\r
4658       break;\r
4659 \r
4660     case IDM_SaveGame:\r
4661       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4662       f = OpenFileDialog(hwnd, "a", defName,\r
4663                          appData.oldSaveStyle ? "gam" : "pgn",\r
4664                          GAME_FILT,\r
4665                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4666       if (f != NULL) {\r
4667         SaveGame(f, 0, "");\r
4668       }\r
4669       break;\r
4670 \r
4671     case IDM_SavePosition:\r
4672       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4673       f = OpenFileDialog(hwnd, "a", defName,\r
4674                          appData.oldSaveStyle ? "pos" : "fen",\r
4675                          POSITION_FILT,\r
4676                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4677       if (f != NULL) {\r
4678         SavePosition(f, 0, "");\r
4679       }\r
4680       break;\r
4681 \r
4682     case IDM_SaveDiagram:\r
4683       defName = "diagram";\r
4684       f = OpenFileDialog(hwnd, "wb", defName,\r
4685                          "bmp",\r
4686                          DIAGRAM_FILT,\r
4687                          "Save Diagram to File", NULL, fileTitle, NULL);\r
4688       if (f != NULL) {\r
4689         SaveDiagram(f);\r
4690       }\r
4691       break;\r
4692 \r
4693     case IDM_CopyGame:\r
4694       CopyGameToClipboard();\r
4695       break;\r
4696 \r
4697     case IDM_PasteGame:\r
4698       PasteGameFromClipboard();\r
4699       break;\r
4700 \r
4701     case IDM_CopyGameListToClipboard:\r
4702       CopyGameListToClipboard();\r
4703       break;\r
4704 \r
4705     /* [AS] Autodetect FEN or PGN data */\r
4706     case IDM_PasteAny:\r
4707       PasteGameOrFENFromClipboard();\r
4708       break;\r
4709 \r
4710     /* [AS] Move history */\r
4711     case IDM_ShowMoveHistory:\r
4712         if( MoveHistoryIsUp() ) {\r
4713             MoveHistoryPopDown();\r
4714         }\r
4715         else {\r
4716             MoveHistoryPopUp();\r
4717         }\r
4718         break;\r
4719 \r
4720     /* [AS] Eval graph */\r
4721     case IDM_ShowEvalGraph:\r
4722         if( EvalGraphIsUp() ) {\r
4723             EvalGraphPopDown();\r
4724         }\r
4725         else {\r
4726             EvalGraphPopUp();\r
4727             SetFocus(hwndMain);\r
4728         }\r
4729         break;\r
4730 \r
4731     /* [AS] Engine output */\r
4732     case IDM_ShowEngineOutput:\r
4733         if( EngineOutputIsUp() ) {\r
4734             EngineOutputPopDown();\r
4735         }\r
4736         else {\r
4737             EngineOutputPopUp();\r
4738         }\r
4739         break;\r
4740 \r
4741     /* [AS] User adjudication */\r
4742     case IDM_UserAdjudication_White:\r
4743         UserAdjudicationEvent( +1 );\r
4744         break;\r
4745 \r
4746     case IDM_UserAdjudication_Black:\r
4747         UserAdjudicationEvent( -1 );\r
4748         break;\r
4749 \r
4750     case IDM_UserAdjudication_Draw:\r
4751         UserAdjudicationEvent( 0 );\r
4752         break;\r
4753 \r
4754     /* [AS] Game list options dialog */\r
4755     case IDM_GameListOptions:\r
4756       GameListOptions();\r
4757       break;\r
4758 \r
4759     case IDM_NewChat:\r
4760       ChatPopUp(NULL);\r
4761       break;\r
4762 \r
4763     case IDM_CopyPosition:\r
4764       CopyFENToClipboard();\r
4765       break;\r
4766 \r
4767     case IDM_PastePosition:\r
4768       PasteFENFromClipboard();\r
4769       break;\r
4770 \r
4771     case IDM_MailMove:\r
4772       MailMoveEvent();\r
4773       break;\r
4774 \r
4775     case IDM_ReloadCMailMsg:\r
4776       Reset(TRUE, TRUE);\r
4777       ReloadCmailMsgEvent(FALSE);\r
4778       break;\r
4779 \r
4780     case IDM_Minimize:\r
4781       ShowWindow(hwnd, SW_MINIMIZE);\r
4782       break;\r
4783 \r
4784     case IDM_Exit:\r
4785       ExitEvent(0);\r
4786       break;\r
4787 \r
4788     case IDM_MachineWhite:\r
4789       MachineWhiteEvent();\r
4790       /*\r
4791        * refresh the tags dialog only if it's visible\r
4792        */\r
4793       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4794           char *tags;\r
4795           tags = PGNTags(&gameInfo);\r
4796           TagsPopUp(tags, CmailMsg());\r
4797           free(tags);\r
4798       }\r
4799       SAY("computer starts playing white");\r
4800       break;\r
4801 \r
4802     case IDM_MachineBlack:\r
4803       MachineBlackEvent();\r
4804       /*\r
4805        * refresh the tags dialog only if it's visible\r
4806        */\r
4807       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4808           char *tags;\r
4809           tags = PGNTags(&gameInfo);\r
4810           TagsPopUp(tags, CmailMsg());\r
4811           free(tags);\r
4812       }\r
4813       SAY("computer starts playing black");\r
4814       break;\r
4815 \r
4816     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4817       if(gameMode != BeginningOfGame) break; // allow menu item to remain enabled for better mode highligting\r
4818       matchMode = 2;// distinguish from command-line-triggered case (matchMode=1)\r
4819       appData.matchGames = appData.defaultMatchGames;\r
4820       matchGame = 1;\r
4821 \r
4822     case IDM_TwoMachines:\r
4823       TwoMachinesEvent();\r
4824       /*\r
4825        * refresh the tags dialog only if it's visible\r
4826        */\r
4827       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4828           char *tags;\r
4829           tags = PGNTags(&gameInfo);\r
4830           TagsPopUp(tags, CmailMsg());\r
4831           free(tags);\r
4832       }\r
4833       SAY("computer starts playing both sides");\r
4834       break;\r
4835 \r
4836     case IDM_AnalysisMode:\r
4837       if (!first.analysisSupport) {\r
4838         sprintf(buf, _("%s does not support analysis"), first.tidy);\r
4839         DisplayError(buf, 0);\r
4840       } else {\r
4841         SAY("analyzing current position");\r
4842         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4843         if (appData.icsActive) {\r
4844                if (gameMode != IcsObserving) {\r
4845                        sprintf(buf, "You are not observing a game");\r
4846                        DisplayError(buf, 0);\r
4847                        /* secure check */\r
4848                        if (appData.icsEngineAnalyze) {\r
4849                                if (appData.debugMode) \r
4850                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4851                                ExitAnalyzeMode();\r
4852                                ModeHighlight();\r
4853                                break;\r
4854                        }\r
4855                        break;\r
4856                } else {\r
4857                        /* if enable, user want disable icsEngineAnalyze */\r
4858                        if (appData.icsEngineAnalyze) {\r
4859                                ExitAnalyzeMode();\r
4860                                ModeHighlight();\r
4861                                break;\r
4862                        }\r
4863                        appData.icsEngineAnalyze = TRUE;\r
4864                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4865                }\r
4866         } \r
4867         if (!appData.showThinking) ToggleShowThinking();\r
4868         AnalyzeModeEvent();\r
4869       }\r
4870       break;\r
4871 \r
4872     case IDM_AnalyzeFile:\r
4873       if (!first.analysisSupport) {\r
4874         char buf[MSG_SIZ];\r
4875         sprintf(buf, _("%s does not support analysis"), first.tidy);\r
4876         DisplayError(buf, 0);\r
4877       } else {\r
4878         if (!appData.showThinking) ToggleShowThinking();\r
4879         AnalyzeFileEvent();\r
4880         LoadGameDialog(hwnd, _("Analyze Game from File"));\r
4881         AnalysisPeriodicEvent(1);\r
4882       }\r
4883       break;\r
4884 \r
4885     case IDM_IcsClient:\r
4886       IcsClientEvent();\r
4887       break;\r
4888 \r
4889     case IDM_EditGame:\r
4890       EditGameEvent();\r
4891       SAY("edit game");\r
4892       break;\r
4893 \r
4894     case IDM_EditPosition:\r
4895       EditPositionEvent();\r
4896       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
4897       break;\r
4898 \r
4899     case IDM_Training:\r
4900       TrainingEvent();\r
4901       break;\r
4902 \r
4903     case IDM_ShowGameList:\r
4904       ShowGameListProc();\r
4905       break;\r
4906 \r
4907     case IDM_EditTags:\r
4908       EditTagsProc();\r
4909       break;\r
4910 \r
4911     case IDM_EditComment:\r
4912       if (commentUp && editComment) {\r
4913         CommentPopDown();\r
4914       } else {\r
4915         EditCommentEvent();\r
4916       }\r
4917       break;\r
4918 \r
4919     case IDM_Pause:\r
4920       PauseEvent();\r
4921       break;\r
4922 \r
4923     case IDM_Accept:\r
4924       AcceptEvent();\r
4925       break;\r
4926 \r
4927     case IDM_Decline:\r
4928       DeclineEvent();\r
4929       break;\r
4930 \r
4931     case IDM_Rematch:\r
4932       RematchEvent();\r
4933       break;\r
4934 \r
4935     case IDM_CallFlag:\r
4936       CallFlagEvent();\r
4937       break;\r
4938 \r
4939     case IDM_Draw:\r
4940       DrawEvent();\r
4941       break;\r
4942 \r
4943     case IDM_Adjourn:\r
4944       AdjournEvent();\r
4945       break;\r
4946 \r
4947     case IDM_Abort:\r
4948       AbortEvent();\r
4949       break;\r
4950 \r
4951     case IDM_Resign:\r
4952       ResignEvent();\r
4953       break;\r
4954 \r
4955     case IDM_StopObserving:\r
4956       StopObservingEvent();\r
4957       break;\r
4958 \r
4959     case IDM_StopExamining:\r
4960       StopExaminingEvent();\r
4961       break;\r
4962 \r
4963     case IDM_Upload:\r
4964       UploadGameEvent();\r
4965       break;\r
4966 \r
4967     case IDM_TypeInMove:\r
4968       PopUpMoveDialog('\000');\r
4969       break;\r
4970 \r
4971     case IDM_TypeInName:\r
4972       PopUpNameDialog('\000');\r
4973       break;\r
4974 \r
4975     case IDM_Backward:\r
4976       BackwardEvent();\r
4977       SetFocus(hwndMain);\r
4978       break;\r
4979 \r
4980     JAWS_MENU_ITEMS\r
4981 \r
4982     case IDM_Forward:\r
4983       ForwardEvent();\r
4984       SetFocus(hwndMain);\r
4985       break;\r
4986 \r
4987     case IDM_ToStart:\r
4988       ToStartEvent();\r
4989       SetFocus(hwndMain);\r
4990       break;\r
4991 \r
4992     case IDM_ToEnd:\r
4993       ToEndEvent();\r
4994       SetFocus(hwndMain);\r
4995       break;\r
4996 \r
4997     case IDM_Revert:\r
4998       RevertEvent(FALSE);\r
4999       break;\r
5000 \r
5001     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5002       RevertEvent(TRUE);\r
5003       break;\r
5004 \r
5005     case IDM_TruncateGame:\r
5006       TruncateGameEvent();\r
5007       break;\r
5008 \r
5009     case IDM_MoveNow:\r
5010       MoveNowEvent();\r
5011       break;\r
5012 \r
5013     case IDM_RetractMove:\r
5014       RetractMoveEvent();\r
5015       break;\r
5016 \r
5017     case IDM_FlipView:\r
5018       flipView = !flipView;\r
5019       DrawPosition(FALSE, NULL);\r
5020       break;\r
5021 \r
5022     case IDM_FlipClock:\r
5023       flipClock = !flipClock;\r
5024       DisplayBothClocks();\r
5025       DrawPosition(FALSE, NULL);\r
5026       break;\r
5027 \r
5028     case IDM_MuteSounds:\r
5029       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5030       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5031                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5032       break;\r
5033 \r
5034     case IDM_GeneralOptions:\r
5035       GeneralOptionsPopup(hwnd);\r
5036       DrawPosition(TRUE, NULL);\r
5037       break;\r
5038 \r
5039     case IDM_BoardOptions:\r
5040       BoardOptionsPopup(hwnd);\r
5041       break;\r
5042 \r
5043     case IDM_EnginePlayOptions:\r
5044       EnginePlayOptionsPopup(hwnd);\r
5045       break;\r
5046 \r
5047     case IDM_Engine1Options:\r
5048       EngineOptionsPopup(hwnd, &first);\r
5049       break;\r
5050 \r
5051     case IDM_Engine2Options:\r
5052       EngineOptionsPopup(hwnd, &second);\r
5053       break;\r
5054 \r
5055     case IDM_OptionsUCI:\r
5056       UciOptionsPopup(hwnd);\r
5057       break;\r
5058 \r
5059     case IDM_IcsOptions:\r
5060       IcsOptionsPopup(hwnd);\r
5061       break;\r
5062 \r
5063     case IDM_Fonts:\r
5064       FontsOptionsPopup(hwnd);\r
5065       break;\r
5066 \r
5067     case IDM_Sounds:\r
5068       SoundOptionsPopup(hwnd);\r
5069       break;\r
5070 \r
5071     case IDM_CommPort:\r
5072       CommPortOptionsPopup(hwnd);\r
5073       break;\r
5074 \r
5075     case IDM_LoadOptions:\r
5076       LoadOptionsPopup(hwnd);\r
5077       break;\r
5078 \r
5079     case IDM_SaveOptions:\r
5080       SaveOptionsPopup(hwnd);\r
5081       break;\r
5082 \r
5083     case IDM_TimeControl:\r
5084       TimeControlOptionsPopup(hwnd);\r
5085       break;\r
5086 \r
5087     case IDM_SaveSettings:\r
5088       SaveSettings(settingsFileName);\r
5089       break;\r
5090 \r
5091     case IDM_SaveSettingsOnExit:\r
5092       saveSettingsOnExit = !saveSettingsOnExit;\r
5093       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5094                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5095                                          MF_CHECKED : MF_UNCHECKED));\r
5096       break;\r
5097 \r
5098     case IDM_Hint:\r
5099       HintEvent();\r
5100       break;\r
5101 \r
5102     case IDM_Book:\r
5103       BookEvent();\r
5104       break;\r
5105 \r
5106     case IDM_AboutGame:\r
5107       AboutGameEvent();\r
5108       break;\r
5109 \r
5110     case IDM_Debug:\r
5111       appData.debugMode = !appData.debugMode;\r
5112       if (appData.debugMode) {\r
5113         char dir[MSG_SIZ];\r
5114         GetCurrentDirectory(MSG_SIZ, dir);\r
5115         SetCurrentDirectory(installDir);\r
5116         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5117         SetCurrentDirectory(dir);\r
5118         setbuf(debugFP, NULL);\r
5119       } else {\r
5120         fclose(debugFP);\r
5121         debugFP = NULL;\r
5122       }\r
5123       break;\r
5124 \r
5125     case IDM_HELPCONTENTS:\r
5126       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5127           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5128           MessageBox (GetFocus(),\r
5129                     _("Unable to activate help"),\r
5130                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5131       }\r
5132       break;\r
5133 \r
5134     case IDM_HELPSEARCH:\r
5135         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5136             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5137         MessageBox (GetFocus(),\r
5138                     _("Unable to activate help"),\r
5139                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5140       }\r
5141       break;\r
5142 \r
5143     case IDM_HELPHELP:\r
5144       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5145         MessageBox (GetFocus(),\r
5146                     _("Unable to activate help"),\r
5147                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5148       }\r
5149       break;\r
5150 \r
5151     case IDM_ABOUT:\r
5152       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5153       DialogBox(hInst, \r
5154         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5155         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5156       FreeProcInstance(lpProc);\r
5157       break;\r
5158 \r
5159     case IDM_DirectCommand1:\r
5160       AskQuestionEvent(_("Direct Command"),\r
5161                        _("Send to chess program:"), "", "1");\r
5162       break;\r
5163     case IDM_DirectCommand2:\r
5164       AskQuestionEvent(_("Direct Command"),\r
5165                        _("Send to second chess program:"), "", "2");\r
5166       break;\r
5167 \r
5168     case EP_WhitePawn:\r
5169       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5170       fromX = fromY = -1;\r
5171       break;\r
5172 \r
5173     case EP_WhiteKnight:\r
5174       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5175       fromX = fromY = -1;\r
5176       break;\r
5177 \r
5178     case EP_WhiteBishop:\r
5179       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5180       fromX = fromY = -1;\r
5181       break;\r
5182 \r
5183     case EP_WhiteRook:\r
5184       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5185       fromX = fromY = -1;\r
5186       break;\r
5187 \r
5188     case EP_WhiteQueen:\r
5189       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5190       fromX = fromY = -1;\r
5191       break;\r
5192 \r
5193     case EP_WhiteFerz:\r
5194       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5195       fromX = fromY = -1;\r
5196       break;\r
5197 \r
5198     case EP_WhiteWazir:\r
5199       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5200       fromX = fromY = -1;\r
5201       break;\r
5202 \r
5203     case EP_WhiteAlfil:\r
5204       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5205       fromX = fromY = -1;\r
5206       break;\r
5207 \r
5208     case EP_WhiteCannon:\r
5209       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5210       fromX = fromY = -1;\r
5211       break;\r
5212 \r
5213     case EP_WhiteCardinal:\r
5214       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5215       fromX = fromY = -1;\r
5216       break;\r
5217 \r
5218     case EP_WhiteMarshall:\r
5219       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5220       fromX = fromY = -1;\r
5221       break;\r
5222 \r
5223     case EP_WhiteKing:\r
5224       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5225       fromX = fromY = -1;\r
5226       break;\r
5227 \r
5228     case EP_BlackPawn:\r
5229       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5230       fromX = fromY = -1;\r
5231       break;\r
5232 \r
5233     case EP_BlackKnight:\r
5234       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5235       fromX = fromY = -1;\r
5236       break;\r
5237 \r
5238     case EP_BlackBishop:\r
5239       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5240       fromX = fromY = -1;\r
5241       break;\r
5242 \r
5243     case EP_BlackRook:\r
5244       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5245       fromX = fromY = -1;\r
5246       break;\r
5247 \r
5248     case EP_BlackQueen:\r
5249       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5250       fromX = fromY = -1;\r
5251       break;\r
5252 \r
5253     case EP_BlackFerz:\r
5254       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5255       fromX = fromY = -1;\r
5256       break;\r
5257 \r
5258     case EP_BlackWazir:\r
5259       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5260       fromX = fromY = -1;\r
5261       break;\r
5262 \r
5263     case EP_BlackAlfil:\r
5264       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5265       fromX = fromY = -1;\r
5266       break;\r
5267 \r
5268     case EP_BlackCannon:\r
5269       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5270       fromX = fromY = -1;\r
5271       break;\r
5272 \r
5273     case EP_BlackCardinal:\r
5274       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5275       fromX = fromY = -1;\r
5276       break;\r
5277 \r
5278     case EP_BlackMarshall:\r
5279       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5280       fromX = fromY = -1;\r
5281       break;\r
5282 \r
5283     case EP_BlackKing:\r
5284       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5285       fromX = fromY = -1;\r
5286       break;\r
5287 \r
5288     case EP_EmptySquare:\r
5289       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5290       fromX = fromY = -1;\r
5291       break;\r
5292 \r
5293     case EP_ClearBoard:\r
5294       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5295       fromX = fromY = -1;\r
5296       break;\r
5297 \r
5298     case EP_White:\r
5299       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5300       fromX = fromY = -1;\r
5301       break;\r
5302 \r
5303     case EP_Black:\r
5304       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5305       fromX = fromY = -1;\r
5306       break;\r
5307 \r
5308     case EP_Promote:\r
5309       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5310       fromX = fromY = -1;\r
5311       break;\r
5312 \r
5313     case EP_Demote:\r
5314       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5315       fromX = fromY = -1;\r
5316       break;\r
5317 \r
5318     case DP_Pawn:\r
5319       DropMenuEvent(WhitePawn, fromX, fromY);\r
5320       fromX = fromY = -1;\r
5321       break;\r
5322 \r
5323     case DP_Knight:\r
5324       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5325       fromX = fromY = -1;\r
5326       break;\r
5327 \r
5328     case DP_Bishop:\r
5329       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5330       fromX = fromY = -1;\r
5331       break;\r
5332 \r
5333     case DP_Rook:\r
5334       DropMenuEvent(WhiteRook, fromX, fromY);\r
5335       fromX = fromY = -1;\r
5336       break;\r
5337 \r
5338     case DP_Queen:\r
5339       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5340       fromX = fromY = -1;\r
5341       break;\r
5342 \r
5343     case IDM_English:\r
5344       barbaric = 0;\r
5345       TranslateMenus(0);\r
5346       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5347       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5348       lastChecked = wmId;\r
5349       break;\r
5350 \r
5351     default:\r
5352       if(wmId > IDM_English && wmId < IDM_English+5) {\r
5353           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5354           TranslateMenus(0);\r
5355           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5356           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5357           lastChecked = wmId;\r
5358           break;\r
5359       }\r
5360       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5361     }\r
5362     break;\r
5363 \r
5364   case WM_TIMER:\r
5365     switch (wParam) {\r
5366     case CLOCK_TIMER_ID:\r
5367       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5368       clockTimerEvent = 0;\r
5369       DecrementClocks(); /* call into back end */\r
5370       break;\r
5371     case LOAD_GAME_TIMER_ID:\r
5372       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5373       loadGameTimerEvent = 0;\r
5374       AutoPlayGameLoop(); /* call into back end */\r
5375       break;\r
5376     case ANALYSIS_TIMER_ID:\r
5377       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5378                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5379         AnalysisPeriodicEvent(0);\r
5380       } else {\r
5381         KillTimer(hwnd, analysisTimerEvent);\r
5382         analysisTimerEvent = 0;\r
5383       }\r
5384       break;\r
5385     case DELAYED_TIMER_ID:\r
5386       KillTimer(hwnd, delayedTimerEvent);\r
5387       delayedTimerEvent = 0;\r
5388       delayedTimerCallback();\r
5389       break;\r
5390     }\r
5391     break;\r
5392 \r
5393   case WM_USER_Input:\r
5394     InputEvent(hwnd, message, wParam, lParam);\r
5395     break;\r
5396 \r
5397   /* [AS] Also move "attached" child windows */\r
5398   case WM_WINDOWPOSCHANGING:\r
5399 \r
5400     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5401         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5402 \r
5403         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5404             /* Window is moving */\r
5405             RECT rcMain;\r
5406 \r
5407 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5408             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5409             rcMain.right  = wpMain.x + wpMain.width;\r
5410             rcMain.top    = wpMain.y;\r
5411             rcMain.bottom = wpMain.y + wpMain.height;\r
5412             \r
5413             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5414             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5415             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5416             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5417             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5418             wpMain.x = lpwp->x;\r
5419             wpMain.y = lpwp->y;\r
5420         }\r
5421     }\r
5422     break;\r
5423 \r
5424   /* [AS] Snapping */\r
5425   case WM_ENTERSIZEMOVE:\r
5426     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5427     if (hwnd == hwndMain) {\r
5428       doingSizing = TRUE;\r
5429       lastSizing = 0;\r
5430     }\r
5431     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5432     break;\r
5433 \r
5434   case WM_SIZING:\r
5435     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5436     if (hwnd == hwndMain) {\r
5437       lastSizing = wParam;\r
5438     }\r
5439     break;\r
5440 \r
5441   case WM_MOVING:\r
5442     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5443       return OnMoving( &sd, hwnd, wParam, lParam );\r
5444 \r
5445   case WM_EXITSIZEMOVE:\r
5446     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5447     if (hwnd == hwndMain) {\r
5448       RECT client;\r
5449       doingSizing = FALSE;\r
5450       InvalidateRect(hwnd, &boardRect, FALSE);\r
5451       GetClientRect(hwnd, &client);\r
5452       ResizeBoard(client.right, client.bottom, lastSizing);\r
5453       lastSizing = 0;\r
5454       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5455     }\r
5456     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5457     break;\r
5458 \r
5459   case WM_DESTROY: /* message: window being destroyed */\r
5460     PostQuitMessage(0);\r
5461     break;\r
5462 \r
5463   case WM_CLOSE:\r
5464     if (hwnd == hwndMain) {\r
5465       ExitEvent(0);\r
5466     }\r
5467     break;\r
5468 \r
5469   default:      /* Passes it on if unprocessed */\r
5470     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5471   }\r
5472   return 0;\r
5473 }\r
5474 \r
5475 /*---------------------------------------------------------------------------*\\r
5476  *\r
5477  * Misc utility routines\r
5478  *\r
5479 \*---------------------------------------------------------------------------*/\r
5480 \r
5481 /*\r
5482  * Decent random number generator, at least not as bad as Windows\r
5483  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5484  */\r
5485 unsigned int randstate;\r
5486 \r
5487 int\r
5488 myrandom(void)\r
5489 {\r
5490   randstate = randstate * 1664525 + 1013904223;\r
5491   return (int) randstate & 0x7fffffff;\r
5492 }\r
5493 \r
5494 void\r
5495 mysrandom(unsigned int seed)\r
5496 {\r
5497   randstate = seed;\r
5498 }\r
5499 \r
5500 \r
5501 /* \r
5502  * returns TRUE if user selects a different color, FALSE otherwise \r
5503  */\r
5504 \r
5505 BOOL\r
5506 ChangeColor(HWND hwnd, COLORREF *which)\r
5507 {\r
5508   static BOOL firstTime = TRUE;\r
5509   static DWORD customColors[16];\r
5510   CHOOSECOLOR cc;\r
5511   COLORREF newcolor;\r
5512   int i;\r
5513   ColorClass ccl;\r
5514 \r
5515   if (firstTime) {\r
5516     /* Make initial colors in use available as custom colors */\r
5517     /* Should we put the compiled-in defaults here instead? */\r
5518     i = 0;\r
5519     customColors[i++] = lightSquareColor & 0xffffff;\r
5520     customColors[i++] = darkSquareColor & 0xffffff;\r
5521     customColors[i++] = whitePieceColor & 0xffffff;\r
5522     customColors[i++] = blackPieceColor & 0xffffff;\r
5523     customColors[i++] = highlightSquareColor & 0xffffff;\r
5524     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5525 \r
5526     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5527       customColors[i++] = textAttribs[ccl].color;\r
5528     }\r
5529     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5530     firstTime = FALSE;\r
5531   }\r
5532 \r
5533   cc.lStructSize = sizeof(cc);\r
5534   cc.hwndOwner = hwnd;\r
5535   cc.hInstance = NULL;\r
5536   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5537   cc.lpCustColors = (LPDWORD) customColors;\r
5538   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5539 \r
5540   if (!ChooseColor(&cc)) return FALSE;\r
5541 \r
5542   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5543   if (newcolor == *which) return FALSE;\r
5544   *which = newcolor;\r
5545   return TRUE;\r
5546 \r
5547   /*\r
5548   InitDrawingColors();\r
5549   InvalidateRect(hwnd, &boardRect, FALSE);\r
5550   */\r
5551 }\r
5552 \r
5553 BOOLEAN\r
5554 MyLoadSound(MySound *ms)\r
5555 {\r
5556   BOOL ok = FALSE;\r
5557   struct stat st;\r
5558   FILE *f;\r
5559 \r
5560   if (ms->data) free(ms->data);\r
5561   ms->data = NULL;\r
5562 \r
5563   switch (ms->name[0]) {\r
5564   case NULLCHAR:\r
5565     /* Silence */\r
5566     ok = TRUE;\r
5567     break;\r
5568   case '$':\r
5569     /* System sound from Control Panel.  Don't preload here. */\r
5570     ok = TRUE;\r
5571     break;\r
5572   case '!':\r
5573     if (ms->name[1] == NULLCHAR) {\r
5574       /* "!" alone = silence */\r
5575       ok = TRUE;\r
5576     } else {\r
5577       /* Builtin wave resource.  Error if not found. */\r
5578       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5579       if (h == NULL) break;\r
5580       ms->data = (void *)LoadResource(hInst, h);\r
5581       if (h == NULL) break;\r
5582       ok = TRUE;\r
5583     }\r
5584     break;\r
5585   default:\r
5586     /* .wav file.  Error if not found. */\r
5587     f = fopen(ms->name, "rb");\r
5588     if (f == NULL) break;\r
5589     if (fstat(fileno(f), &st) < 0) break;\r
5590     ms->data = malloc(st.st_size);\r
5591     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5592     fclose(f);\r
5593     ok = TRUE;\r
5594     break;\r
5595   }\r
5596   if (!ok) {\r
5597     char buf[MSG_SIZ];\r
5598     sprintf(buf, _("Error loading sound %s"), ms->name);\r
5599     DisplayError(buf, GetLastError());\r
5600   }\r
5601   return ok;\r
5602 }\r
5603 \r
5604 BOOLEAN\r
5605 MyPlaySound(MySound *ms)\r
5606 {\r
5607   BOOLEAN ok = FALSE;\r
5608 \r
5609   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5610   switch (ms->name[0]) {\r
5611   case NULLCHAR:\r
5612         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5613     /* Silence */\r
5614     ok = TRUE;\r
5615     break;\r
5616   case '$':\r
5617     /* System sound from Control Panel (deprecated feature).\r
5618        "$" alone or an unset sound name gets default beep (still in use). */\r
5619     if (ms->name[1]) {\r
5620       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5621     }\r
5622     if (!ok) ok = MessageBeep(MB_OK);\r
5623     break; \r
5624   case '!':\r
5625     /* Builtin wave resource, or "!" alone for silence */\r
5626     if (ms->name[1]) {\r
5627       if (ms->data == NULL) return FALSE;\r
5628       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5629     } else {\r
5630       ok = TRUE;\r
5631     }\r
5632     break;\r
5633   default:\r
5634     /* .wav file.  Error if not found. */\r
5635     if (ms->data == NULL) return FALSE;\r
5636     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5637     break;\r
5638   }\r
5639   /* Don't print an error: this can happen innocently if the sound driver\r
5640      is busy; for instance, if another instance of WinBoard is playing\r
5641      a sound at about the same time. */\r
5642   return ok;\r
5643 }\r
5644 \r
5645 \r
5646 LRESULT CALLBACK\r
5647 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5648 {\r
5649   BOOL ok;\r
5650   OPENFILENAME *ofn;\r
5651   static UINT *number; /* gross that this is static */\r
5652 \r
5653   switch (message) {\r
5654   case WM_INITDIALOG: /* message: initialize dialog box */\r
5655     /* Center the dialog over the application window */\r
5656     ofn = (OPENFILENAME *) lParam;\r
5657     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5658       number = (UINT *) ofn->lCustData;\r
5659       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5660     } else {\r
5661       number = NULL;\r
5662     }\r
5663     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5664     Translate(hDlg, 1536);\r
5665     return FALSE;  /* Allow for further processing */\r
5666 \r
5667   case WM_COMMAND:\r
5668     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5669       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5670     }\r
5671     return FALSE;  /* Allow for further processing */\r
5672   }\r
5673   return FALSE;\r
5674 }\r
5675 \r
5676 UINT APIENTRY\r
5677 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5678 {\r
5679   static UINT *number;\r
5680   OPENFILENAME *ofname;\r
5681   OFNOTIFY *ofnot;\r
5682   switch (uiMsg) {\r
5683   case WM_INITDIALOG:\r
5684     Translate(hdlg, DLG_IndexNumber);\r
5685     ofname = (OPENFILENAME *)lParam;\r
5686     number = (UINT *)(ofname->lCustData);\r
5687     break;\r
5688   case WM_NOTIFY:\r
5689     ofnot = (OFNOTIFY *)lParam;\r
5690     if (ofnot->hdr.code == CDN_FILEOK) {\r
5691       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5692     }\r
5693     break;\r
5694   }\r
5695   return 0;\r
5696 }\r
5697 \r
5698 \r
5699 FILE *\r
5700 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5701                char *nameFilt, char *dlgTitle, UINT *number,\r
5702                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5703 {\r
5704   OPENFILENAME openFileName;\r
5705   char buf1[MSG_SIZ];\r
5706   FILE *f;\r
5707 \r
5708   if (fileName == NULL) fileName = buf1;\r
5709   if (defName == NULL) {\r
5710     safeStrCpy(fileName, "*.", sizeof(fileName)/sizeof(fileName[0]) );\r
5711     strcat(fileName, defExt);\r
5712   } else {\r
5713     safeStrCpy(fileName, defName, sizeof(fileName)/sizeof(fileName[0]) );\r
5714   }\r
5715     if (fileTitle) safeStrCpy(fileTitle, "", sizeof(fileTitle)/sizeof(fileTitle[0]) );\r
5716   if (number) *number = 0;\r
5717 \r
5718   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5719   openFileName.hwndOwner         = hwnd;\r
5720   openFileName.hInstance         = (HANDLE) hInst;\r
5721   openFileName.lpstrFilter       = nameFilt;\r
5722   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5723   openFileName.nMaxCustFilter    = 0L;\r
5724   openFileName.nFilterIndex      = 1L;\r
5725   openFileName.lpstrFile         = fileName;\r
5726   openFileName.nMaxFile          = MSG_SIZ;\r
5727   openFileName.lpstrFileTitle    = fileTitle;\r
5728   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5729   openFileName.lpstrInitialDir   = NULL;\r
5730   openFileName.lpstrTitle        = dlgTitle;\r
5731   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5732     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5733     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5734     | (oldDialog ? 0 : OFN_EXPLORER);\r
5735   openFileName.nFileOffset       = 0;\r
5736   openFileName.nFileExtension    = 0;\r
5737   openFileName.lpstrDefExt       = defExt;\r
5738   openFileName.lCustData         = (LONG) number;\r
5739   openFileName.lpfnHook          = oldDialog ?\r
5740     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5741   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5742 \r
5743   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5744                         GetOpenFileName(&openFileName)) {\r
5745     /* open the file */\r
5746     f = fopen(openFileName.lpstrFile, write);\r
5747     if (f == NULL) {\r
5748       MessageBox(hwnd, _("File open failed"), NULL,\r
5749                  MB_OK|MB_ICONEXCLAMATION);\r
5750       return NULL;\r
5751     }\r
5752   } else {\r
5753     int err = CommDlgExtendedError();\r
5754     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5755     return FALSE;\r
5756   }\r
5757   return f;\r
5758 }\r
5759 \r
5760 \r
5761 \r
5762 VOID APIENTRY\r
5763 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5764 {\r
5765   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5766 \r
5767   /*\r
5768    * Get the first pop-up menu in the menu template. This is the\r
5769    * menu that TrackPopupMenu displays.\r
5770    */\r
5771   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5772 \r
5773   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5774 \r
5775   /*\r
5776    * TrackPopup uses screen coordinates, so convert the\r
5777    * coordinates of the mouse click to screen coordinates.\r
5778    */\r
5779   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5780 \r
5781   /* Draw and track the floating pop-up menu. */\r
5782   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5783                  pt.x, pt.y, 0, hwnd, NULL);\r
5784 \r
5785   /* Destroy the menu.*/\r
5786   DestroyMenu(hmenu);\r
5787 }\r
5788    \r
5789 typedef struct {\r
5790   HWND hDlg, hText;\r
5791   int sizeX, sizeY, newSizeX, newSizeY;\r
5792   HDWP hdwp;\r
5793 } ResizeEditPlusButtonsClosure;\r
5794 \r
5795 BOOL CALLBACK\r
5796 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5797 {\r
5798   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5799   RECT rect;\r
5800   POINT pt;\r
5801 \r
5802   if (hChild == cl->hText) return TRUE;\r
5803   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5804   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5805   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5806   ScreenToClient(cl->hDlg, &pt);\r
5807   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5808     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5809   return TRUE;\r
5810 }\r
5811 \r
5812 /* Resize a dialog that has a (rich) edit field filling most of\r
5813    the top, with a row of buttons below */\r
5814 VOID\r
5815 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5816 {\r
5817   RECT rectText;\r
5818   int newTextHeight, newTextWidth;\r
5819   ResizeEditPlusButtonsClosure cl;\r
5820   \r
5821   /*if (IsIconic(hDlg)) return;*/\r
5822   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5823   \r
5824   cl.hdwp = BeginDeferWindowPos(8);\r
5825 \r
5826   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5827   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5828   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5829   if (newTextHeight < 0) {\r
5830     newSizeY += -newTextHeight;\r
5831     newTextHeight = 0;\r
5832   }\r
5833   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5834     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5835 \r
5836   cl.hDlg = hDlg;\r
5837   cl.hText = hText;\r
5838   cl.sizeX = sizeX;\r
5839   cl.sizeY = sizeY;\r
5840   cl.newSizeX = newSizeX;\r
5841   cl.newSizeY = newSizeY;\r
5842   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5843 \r
5844   EndDeferWindowPos(cl.hdwp);\r
5845 }\r
5846 \r
5847 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5848 {\r
5849     RECT    rChild, rParent;\r
5850     int     wChild, hChild, wParent, hParent;\r
5851     int     wScreen, hScreen, xNew, yNew;\r
5852     HDC     hdc;\r
5853 \r
5854     /* Get the Height and Width of the child window */\r
5855     GetWindowRect (hwndChild, &rChild);\r
5856     wChild = rChild.right - rChild.left;\r
5857     hChild = rChild.bottom - rChild.top;\r
5858 \r
5859     /* Get the Height and Width of the parent window */\r
5860     GetWindowRect (hwndParent, &rParent);\r
5861     wParent = rParent.right - rParent.left;\r
5862     hParent = rParent.bottom - rParent.top;\r
5863 \r
5864     /* Get the display limits */\r
5865     hdc = GetDC (hwndChild);\r
5866     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5867     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5868     ReleaseDC(hwndChild, hdc);\r
5869 \r
5870     /* Calculate new X position, then adjust for screen */\r
5871     xNew = rParent.left + ((wParent - wChild) /2);\r
5872     if (xNew < 0) {\r
5873         xNew = 0;\r
5874     } else if ((xNew+wChild) > wScreen) {\r
5875         xNew = wScreen - wChild;\r
5876     }\r
5877 \r
5878     /* Calculate new Y position, then adjust for screen */\r
5879     if( mode == 0 ) {\r
5880         yNew = rParent.top  + ((hParent - hChild) /2);\r
5881     }\r
5882     else {\r
5883         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5884     }\r
5885 \r
5886     if (yNew < 0) {\r
5887         yNew = 0;\r
5888     } else if ((yNew+hChild) > hScreen) {\r
5889         yNew = hScreen - hChild;\r
5890     }\r
5891 \r
5892     /* Set it, and return */\r
5893     return SetWindowPos (hwndChild, NULL,\r
5894                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5895 }\r
5896 \r
5897 /* Center one window over another */\r
5898 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
5899 {\r
5900     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
5901 }\r
5902 \r
5903 /*---------------------------------------------------------------------------*\\r
5904  *\r
5905  * Startup Dialog functions\r
5906  *\r
5907 \*---------------------------------------------------------------------------*/\r
5908 void\r
5909 InitComboStrings(HANDLE hwndCombo, char **cd)\r
5910 {\r
5911   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5912 \r
5913   while (*cd != NULL) {\r
5914     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
5915     cd++;\r
5916   }\r
5917 }\r
5918 \r
5919 void\r
5920 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
5921 {\r
5922   char buf1[MAX_ARG_LEN];\r
5923   int len;\r
5924 \r
5925   if (str[0] == '@') {\r
5926     FILE* f = fopen(str + 1, "r");\r
5927     if (f == NULL) {\r
5928       DisplayFatalError(str + 1, errno, 2);\r
5929       return;\r
5930     }\r
5931     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
5932     fclose(f);\r
5933     buf1[len] = NULLCHAR;\r
5934     str = buf1;\r
5935   }\r
5936 \r
5937   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5938 \r
5939   for (;;) {\r
5940     char buf[MSG_SIZ];\r
5941     char *end = strchr(str, '\n');\r
5942     if (end == NULL) return;\r
5943     memcpy(buf, str, end - str);\r
5944     buf[end - str] = NULLCHAR;\r
5945     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
5946     str = end + 1;\r
5947   }\r
5948 }\r
5949 \r
5950 void\r
5951 SetStartupDialogEnables(HWND hDlg)\r
5952 {\r
5953   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
5954     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5955     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
5956   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
5957     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
5958   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
5959     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
5960   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
5961     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
5962   EnableWindow(GetDlgItem(hDlg, IDOK),\r
5963     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5964     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
5965     IsDlgButtonChecked(hDlg, OPT_View));\r
5966 }\r
5967 \r
5968 char *\r
5969 QuoteForFilename(char *filename)\r
5970 {\r
5971   int dquote, space;\r
5972   dquote = strchr(filename, '"') != NULL;\r
5973   space = strchr(filename, ' ') != NULL;\r
5974   if (dquote || space) {\r
5975     if (dquote) {\r
5976       return "'";\r
5977     } else {\r
5978       return "\"";\r
5979     }\r
5980   } else {\r
5981     return "";\r
5982   }\r
5983 }\r
5984 \r
5985 VOID\r
5986 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
5987 {\r
5988   char buf[MSG_SIZ];\r
5989   char *q;\r
5990 \r
5991   InitComboStringsFromOption(hwndCombo, nthnames);\r
5992   q = QuoteForFilename(nthcp);\r
5993   sprintf(buf, "%s%s%s", q, nthcp, q);\r
5994   if (*nthdir != NULLCHAR) {\r
5995     q = QuoteForFilename(nthdir);\r
5996     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
5997   }\r
5998   if (*nthcp == NULLCHAR) {\r
5999     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6000   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6001     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6002     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6003   }\r
6004 }\r
6005 \r
6006 LRESULT CALLBACK\r
6007 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6008 {\r
6009   char buf[MSG_SIZ];\r
6010   HANDLE hwndCombo;\r
6011   char *p;\r
6012 \r
6013   switch (message) {\r
6014   case WM_INITDIALOG:\r
6015     /* Center the dialog */\r
6016     CenterWindow (hDlg, GetDesktopWindow());\r
6017     Translate(hDlg, DLG_Startup);\r
6018     /* Initialize the dialog items */\r
6019     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6020                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6021                   firstChessProgramNames);\r
6022     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6023                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6024                   secondChessProgramNames);\r
6025     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6026     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6027     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6028     if (*appData.icsHelper != NULLCHAR) {\r
6029       char *q = QuoteForFilename(appData.icsHelper);\r
6030       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6031     }\r
6032     if (*appData.icsHost == NULLCHAR) {\r
6033       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6034       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6035     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6036       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6037       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6038     }\r
6039 \r
6040     if (appData.icsActive) {\r
6041       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6042     }\r
6043     else if (appData.noChessProgram) {\r
6044       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6045     }\r
6046     else {\r
6047       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6048     }\r
6049 \r
6050     SetStartupDialogEnables(hDlg);\r
6051     return TRUE;\r
6052 \r
6053   case WM_COMMAND:\r
6054     switch (LOWORD(wParam)) {\r
6055     case IDOK:\r
6056       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6057         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6058         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6059         p = buf;\r
6060         ParseArgs(StringGet, &p);\r
6061         safeStrCpy(buf, "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6062         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6063         p = buf;\r
6064         ParseArgs(StringGet, &p);\r
6065         appData.noChessProgram = FALSE;\r
6066         appData.icsActive = FALSE;\r
6067       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6068         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6069         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6070         p = buf;\r
6071         ParseArgs(StringGet, &p);\r
6072         if (appData.zippyPlay) {\r
6073           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6074           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6075           p = buf;\r
6076           ParseArgs(StringGet, &p);\r
6077         }\r
6078       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6079         appData.noChessProgram = TRUE;\r
6080         appData.icsActive = FALSE;\r
6081       } else {\r
6082         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6083                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6084         return TRUE;\r
6085       }\r
6086       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6087         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6088         p = buf;\r
6089         ParseArgs(StringGet, &p);\r
6090       }\r
6091       EndDialog(hDlg, TRUE);\r
6092       return TRUE;\r
6093 \r
6094     case IDCANCEL:\r
6095       ExitEvent(0);\r
6096       return TRUE;\r
6097 \r
6098     case IDM_HELPCONTENTS:\r
6099       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6100         MessageBox (GetFocus(),\r
6101                     _("Unable to activate help"),\r
6102                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6103       }\r
6104       break;\r
6105 \r
6106     default:\r
6107       SetStartupDialogEnables(hDlg);\r
6108       break;\r
6109     }\r
6110     break;\r
6111   }\r
6112   return FALSE;\r
6113 }\r
6114 \r
6115 /*---------------------------------------------------------------------------*\\r
6116  *\r
6117  * About box dialog functions\r
6118  *\r
6119 \*---------------------------------------------------------------------------*/\r
6120 \r
6121 /* Process messages for "About" dialog box */\r
6122 LRESULT CALLBACK\r
6123 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6124 {\r
6125   switch (message) {\r
6126   case WM_INITDIALOG: /* message: initialize dialog box */\r
6127     /* Center the dialog over the application window */\r
6128     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6129     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6130     Translate(hDlg, ABOUTBOX);\r
6131     JAWS_COPYRIGHT\r
6132     return (TRUE);\r
6133 \r
6134   case WM_COMMAND: /* message: received a command */\r
6135     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6136         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6137       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6138       return (TRUE);\r
6139     }\r
6140     break;\r
6141   }\r
6142   return (FALSE);\r
6143 }\r
6144 \r
6145 /*---------------------------------------------------------------------------*\\r
6146  *\r
6147  * Comment Dialog functions\r
6148  *\r
6149 \*---------------------------------------------------------------------------*/\r
6150 \r
6151 LRESULT CALLBACK\r
6152 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6153 {\r
6154   static HANDLE hwndText = NULL;\r
6155   int len, newSizeX, newSizeY, flags;\r
6156   static int sizeX, sizeY;\r
6157   char *str;\r
6158   RECT rect;\r
6159   MINMAXINFO *mmi;\r
6160 \r
6161   switch (message) {\r
6162   case WM_INITDIALOG: /* message: initialize dialog box */\r
6163     /* Initialize the dialog items */\r
6164     Translate(hDlg, DLG_EditComment);\r
6165     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6166     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6167     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6168     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6169     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6170     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6171     SetWindowText(hDlg, commentTitle);\r
6172     if (editComment) {\r
6173       SetFocus(hwndText);\r
6174     } else {\r
6175       SetFocus(GetDlgItem(hDlg, IDOK));\r
6176     }\r
6177     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6178                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6179                 MAKELPARAM(FALSE, 0));\r
6180     /* Size and position the dialog */\r
6181     if (!commentDialog) {\r
6182       commentDialog = hDlg;\r
6183       flags = SWP_NOZORDER;\r
6184       GetClientRect(hDlg, &rect);\r
6185       sizeX = rect.right;\r
6186       sizeY = rect.bottom;\r
6187       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6188           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6189         WINDOWPLACEMENT wp;\r
6190         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6191         wp.length = sizeof(WINDOWPLACEMENT);\r
6192         wp.flags = 0;\r
6193         wp.showCmd = SW_SHOW;\r
6194         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6195         wp.rcNormalPosition.left = wpComment.x;\r
6196         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6197         wp.rcNormalPosition.top = wpComment.y;\r
6198         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6199         SetWindowPlacement(hDlg, &wp);\r
6200 \r
6201         GetClientRect(hDlg, &rect);\r
6202         newSizeX = rect.right;\r
6203         newSizeY = rect.bottom;\r
6204         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6205                               newSizeX, newSizeY);\r
6206         sizeX = newSizeX;\r
6207         sizeY = newSizeY;\r
6208       }\r
6209     }\r
6210     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );\r
6211     return FALSE;\r
6212 \r
6213   case WM_COMMAND: /* message: received a command */\r
6214     switch (LOWORD(wParam)) {\r
6215     case IDOK:\r
6216       if (editComment) {\r
6217         char *p, *q;\r
6218         /* Read changed options from the dialog box */\r
6219         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6220         len = GetWindowTextLength(hwndText);\r
6221         str = (char *) malloc(len + 1);\r
6222         GetWindowText(hwndText, str, len + 1);\r
6223         p = q = str;\r
6224         while (*q) {\r
6225           if (*q == '\r')\r
6226             q++;\r
6227           else\r
6228             *p++ = *q++;\r
6229         }\r
6230         *p = NULLCHAR;\r
6231         ReplaceComment(commentIndex, str);\r
6232         free(str);\r
6233       }\r
6234       CommentPopDown();\r
6235       return TRUE;\r
6236 \r
6237     case IDCANCEL:\r
6238     case OPT_CancelComment:\r
6239       CommentPopDown();\r
6240       return TRUE;\r
6241 \r
6242     case OPT_ClearComment:\r
6243       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6244       break;\r
6245 \r
6246     case OPT_EditComment:\r
6247       EditCommentEvent();\r
6248       return TRUE;\r
6249 \r
6250     default:\r
6251       break;\r
6252     }\r
6253     break;\r
6254 \r
6255   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6256         if( wParam == OPT_CommentText ) {\r
6257             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6258 \r
6259             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) {\r
6260                 POINTL pt;\r
6261                 LRESULT index;\r
6262 \r
6263                 pt.x = LOWORD( lpMF->lParam );\r
6264                 pt.y = HIWORD( lpMF->lParam );\r
6265 \r
6266                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6267 \r
6268                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6269                 len = GetWindowTextLength(hwndText);\r
6270                 str = (char *) malloc(len + 1);\r
6271                 GetWindowText(hwndText, str, len + 1);\r
6272                 ReplaceComment(commentIndex, str);\r
6273                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6274                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6275                 free(str);\r
6276 \r
6277                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6278                 lpMF->msg = WM_USER;\r
6279 \r
6280                 return TRUE;\r
6281             }\r
6282         }\r
6283         break;\r
6284 \r
6285   case WM_SIZE:\r
6286     newSizeX = LOWORD(lParam);\r
6287     newSizeY = HIWORD(lParam);\r
6288     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6289     sizeX = newSizeX;\r
6290     sizeY = newSizeY;\r
6291     break;\r
6292 \r
6293   case WM_GETMINMAXINFO:\r
6294     /* Prevent resizing window too small */\r
6295     mmi = (MINMAXINFO *) lParam;\r
6296     mmi->ptMinTrackSize.x = 100;\r
6297     mmi->ptMinTrackSize.y = 100;\r
6298     break;\r
6299   }\r
6300   return FALSE;\r
6301 }\r
6302 \r
6303 VOID\r
6304 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6305 {\r
6306   FARPROC lpProc;\r
6307   char *p, *q;\r
6308 \r
6309   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6310 \r
6311   if (str == NULL) str = "";\r
6312   p = (char *) malloc(2 * strlen(str) + 2);\r
6313   q = p;\r
6314   while (*str) {\r
6315     if (*str == '\n') *q++ = '\r';\r
6316     *q++ = *str++;\r
6317   }\r
6318   *q = NULLCHAR;\r
6319   if (commentText != NULL) free(commentText);\r
6320 \r
6321   commentIndex = index;\r
6322   commentTitle = title;\r
6323   commentText = p;\r
6324   editComment = edit;\r
6325 \r
6326   if (commentDialog) {\r
6327     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6328     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6329   } else {\r
6330     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6331     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6332                  hwndMain, (DLGPROC)lpProc);\r
6333     FreeProcInstance(lpProc);\r
6334   }\r
6335   commentUp = TRUE;\r
6336 }\r
6337 \r
6338 \r
6339 /*---------------------------------------------------------------------------*\\r
6340  *\r
6341  * Type-in move dialog functions\r
6342  * \r
6343 \*---------------------------------------------------------------------------*/\r
6344 \r
6345 LRESULT CALLBACK\r
6346 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6347 {\r
6348   char move[MSG_SIZ];\r
6349   HWND hInput;\r
6350   ChessMove moveType;\r
6351   int fromX, fromY, toX, toY;\r
6352   char promoChar;\r
6353 \r
6354   switch (message) {\r
6355   case WM_INITDIALOG:\r
6356     move[0] = (char) lParam;\r
6357     move[1] = NULLCHAR;\r
6358     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6359     Translate(hDlg, DLG_TypeInMove);\r
6360     hInput = GetDlgItem(hDlg, OPT_Move);\r
6361     SetWindowText(hInput, move);\r
6362     SetFocus(hInput);\r
6363     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6364     return FALSE;\r
6365 \r
6366   case WM_COMMAND:\r
6367     switch (LOWORD(wParam)) {\r
6368     case IDOK:\r
6369       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6370       { int n; Board board;\r
6371         // [HGM] FENedit\r
6372         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
6373                 EditPositionPasteFEN(move);\r
6374                 EndDialog(hDlg, TRUE);\r
6375                 return TRUE;\r
6376         }\r
6377         // [HGM] movenum: allow move number to be typed in any mode\r
6378         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
6379           ToNrEvent(2*n-1);\r
6380           EndDialog(hDlg, TRUE);\r
6381           return TRUE;\r
6382         }\r
6383       }\r
6384       if (gameMode != EditGame && currentMove != forwardMostMove && \r
6385         gameMode != Training) {\r
6386         DisplayMoveError(_("Displayed move is not current"));\r
6387       } else {\r
6388 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
6389         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6390           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
6391         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
6392         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6393           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
6394           if (gameMode != Training)\r
6395               forwardMostMove = currentMove;\r
6396           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
6397         } else {\r
6398           DisplayMoveError(_("Could not parse move"));\r
6399         }\r
6400       }\r
6401       EndDialog(hDlg, TRUE);\r
6402       return TRUE;\r
6403     case IDCANCEL:\r
6404       EndDialog(hDlg, FALSE);\r
6405       return TRUE;\r
6406     default:\r
6407       break;\r
6408     }\r
6409     break;\r
6410   }\r
6411   return FALSE;\r
6412 }\r
6413 \r
6414 VOID\r
6415 PopUpMoveDialog(char firstchar)\r
6416 {\r
6417     FARPROC lpProc;\r
6418     \r
6419     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
6420         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
6421         gameMode == AnalyzeMode || gameMode == EditGame || \r
6422         gameMode == EditPosition || gameMode == IcsExamining ||\r
6423         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
6424         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
6425                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
6426                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
6427         gameMode == Training) {\r
6428       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6429       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6430         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6431       FreeProcInstance(lpProc);\r
6432     }\r
6433 }\r
6434 \r
6435 /*---------------------------------------------------------------------------*\\r
6436  *\r
6437  * Type-in name dialog functions\r
6438  * \r
6439 \*---------------------------------------------------------------------------*/\r
6440 \r
6441 LRESULT CALLBACK\r
6442 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6443 {\r
6444   char move[MSG_SIZ];\r
6445   HWND hInput;\r
6446 \r
6447   switch (message) {\r
6448   case WM_INITDIALOG:\r
6449     move[0] = (char) lParam;\r
6450     move[1] = NULLCHAR;\r
6451     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6452     Translate(hDlg, DLG_TypeInName);\r
6453     hInput = GetDlgItem(hDlg, OPT_Name);\r
6454     SetWindowText(hInput, move);\r
6455     SetFocus(hInput);\r
6456     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6457     return FALSE;\r
6458 \r
6459   case WM_COMMAND:\r
6460     switch (LOWORD(wParam)) {\r
6461     case IDOK:\r
6462       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6463       appData.userName = strdup(move);\r
6464       SetUserLogo();\r
6465       SetGameInfo();\r
6466       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6467         sprintf(move, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6468         DisplayTitle(move);\r
6469       }\r
6470 \r
6471 \r
6472       EndDialog(hDlg, TRUE);\r
6473       return TRUE;\r
6474     case IDCANCEL:\r
6475       EndDialog(hDlg, FALSE);\r
6476       return TRUE;\r
6477     default:\r
6478       break;\r
6479     }\r
6480     break;\r
6481   }\r
6482   return FALSE;\r
6483 }\r
6484 \r
6485 VOID\r
6486 PopUpNameDialog(char firstchar)\r
6487 {\r
6488     FARPROC lpProc;\r
6489     \r
6490       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6491       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6492         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6493       FreeProcInstance(lpProc);\r
6494 }\r
6495 \r
6496 /*---------------------------------------------------------------------------*\\r
6497  *\r
6498  *  Error dialogs\r
6499  * \r
6500 \*---------------------------------------------------------------------------*/\r
6501 \r
6502 /* Nonmodal error box */\r
6503 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6504                              WPARAM wParam, LPARAM lParam);\r
6505 \r
6506 VOID\r
6507 ErrorPopUp(char *title, char *content)\r
6508 {\r
6509   FARPROC lpProc;\r
6510   char *p, *q;\r
6511   BOOLEAN modal = hwndMain == NULL;\r
6512 \r
6513   p = content;\r
6514   q = errorMessage;\r
6515   while (*p) {\r
6516     if (*p == '\n') {\r
6517       if (modal) {\r
6518         *q++ = ' ';\r
6519         p++;\r
6520       } else {\r
6521         *q++ = '\r';\r
6522         *q++ = *p++;\r
6523       }\r
6524     } else {\r
6525       *q++ = *p++;\r
6526     }\r
6527   }\r
6528   *q = NULLCHAR;\r
6529   strncpy(errorTitle, title, sizeof(errorTitle));\r
6530   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6531   \r
6532   if (modal) {\r
6533     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6534   } else {\r
6535     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6536     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6537                  hwndMain, (DLGPROC)lpProc);\r
6538     FreeProcInstance(lpProc);\r
6539   }\r
6540 }\r
6541 \r
6542 VOID\r
6543 ErrorPopDown()\r
6544 {\r
6545   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6546   if (errorDialog == NULL) return;\r
6547   DestroyWindow(errorDialog);\r
6548   errorDialog = NULL;\r
6549   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6550 }\r
6551 \r
6552 LRESULT CALLBACK\r
6553 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6554 {\r
6555   HANDLE hwndText;\r
6556   RECT rChild;\r
6557 \r
6558   switch (message) {\r
6559   case WM_INITDIALOG:\r
6560     GetWindowRect(hDlg, &rChild);\r
6561 \r
6562     /*\r
6563     SetWindowPos(hDlg, NULL, rChild.left,\r
6564       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6565       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6566     */\r
6567 \r
6568     /* \r
6569         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6570         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6571         and it doesn't work when you resize the dialog.\r
6572         For now, just give it a default position.\r
6573     */\r
6574     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6575     Translate(hDlg, DLG_Error);\r
6576 \r
6577     errorDialog = hDlg;\r
6578     SetWindowText(hDlg, errorTitle);\r
6579     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6580     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6581     return FALSE;\r
6582 \r
6583   case WM_COMMAND:\r
6584     switch (LOWORD(wParam)) {\r
6585     case IDOK:\r
6586     case IDCANCEL:\r
6587       if (errorDialog == hDlg) errorDialog = NULL;\r
6588       DestroyWindow(hDlg);\r
6589       return TRUE;\r
6590 \r
6591     default:\r
6592       break;\r
6593     }\r
6594     break;\r
6595   }\r
6596   return FALSE;\r
6597 }\r
6598 \r
6599 #ifdef GOTHIC\r
6600 HWND gothicDialog = NULL;\r
6601 \r
6602 LRESULT CALLBACK\r
6603 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6604 {\r
6605   HANDLE hwndText;\r
6606   RECT rChild;\r
6607   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6608 \r
6609   switch (message) {\r
6610   case WM_INITDIALOG:\r
6611     GetWindowRect(hDlg, &rChild);\r
6612 \r
6613     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6614                                                              SWP_NOZORDER);\r
6615 \r
6616     /* \r
6617         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6618         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6619         and it doesn't work when you resize the dialog.\r
6620         For now, just give it a default position.\r
6621     */\r
6622     gothicDialog = hDlg;\r
6623     SetWindowText(hDlg, errorTitle);\r
6624     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6625     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6626     return FALSE;\r
6627 \r
6628   case WM_COMMAND:\r
6629     switch (LOWORD(wParam)) {\r
6630     case IDOK:\r
6631     case IDCANCEL:\r
6632       if (errorDialog == hDlg) errorDialog = NULL;\r
6633       DestroyWindow(hDlg);\r
6634       return TRUE;\r
6635 \r
6636     default:\r
6637       break;\r
6638     }\r
6639     break;\r
6640   }\r
6641   return FALSE;\r
6642 }\r
6643 \r
6644 VOID\r
6645 GothicPopUp(char *title, VariantClass variant)\r
6646 {\r
6647   FARPROC lpProc;\r
6648   static char *lastTitle;\r
6649 \r
6650   strncpy(errorTitle, title, sizeof(errorTitle));\r
6651   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6652 \r
6653   if(lastTitle != title && gothicDialog != NULL) {\r
6654     DestroyWindow(gothicDialog);\r
6655     gothicDialog = NULL;\r
6656   }\r
6657   if(variant != VariantNormal && gothicDialog == NULL) {\r
6658     title = lastTitle;\r
6659     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6660     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6661                  hwndMain, (DLGPROC)lpProc);\r
6662     FreeProcInstance(lpProc);\r
6663   }\r
6664 }\r
6665 #endif\r
6666 \r
6667 /*---------------------------------------------------------------------------*\\r
6668  *\r
6669  *  Ics Interaction console functions\r
6670  *\r
6671 \*---------------------------------------------------------------------------*/\r
6672 \r
6673 #define HISTORY_SIZE 64\r
6674 static char *history[HISTORY_SIZE];\r
6675 int histIn = 0, histP = 0;\r
6676 \r
6677 VOID\r
6678 SaveInHistory(char *cmd)\r
6679 {\r
6680   if (history[histIn] != NULL) {\r
6681     free(history[histIn]);\r
6682     history[histIn] = NULL;\r
6683   }\r
6684   if (*cmd == NULLCHAR) return;\r
6685   history[histIn] = StrSave(cmd);\r
6686   histIn = (histIn + 1) % HISTORY_SIZE;\r
6687   if (history[histIn] != NULL) {\r
6688     free(history[histIn]);\r
6689     history[histIn] = NULL;\r
6690   }\r
6691   histP = histIn;\r
6692 }\r
6693 \r
6694 char *\r
6695 PrevInHistory(char *cmd)\r
6696 {\r
6697   int newhp;\r
6698   if (histP == histIn) {\r
6699     if (history[histIn] != NULL) free(history[histIn]);\r
6700     history[histIn] = StrSave(cmd);\r
6701   }\r
6702   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6703   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6704   histP = newhp;\r
6705   return history[histP];\r
6706 }\r
6707 \r
6708 char *\r
6709 NextInHistory()\r
6710 {\r
6711   if (histP == histIn) return NULL;\r
6712   histP = (histP + 1) % HISTORY_SIZE;\r
6713   return history[histP];   \r
6714 }\r
6715 \r
6716 HMENU\r
6717 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6718 {\r
6719   HMENU hmenu, h;\r
6720   int i = 0;\r
6721   hmenu = LoadMenu(hInst, "TextMenu");\r
6722   h = GetSubMenu(hmenu, 0);\r
6723   while (e->item) {\r
6724     if (strcmp(e->item, "-") == 0) {\r
6725       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6726     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6727       int flags = MF_STRING, j = 0;\r
6728       if (e->item[0] == '|') {\r
6729         flags |= MF_MENUBARBREAK;\r
6730         j++;\r
6731       }\r
6732       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6733       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6734     }\r
6735     e++;\r
6736     i++;\r
6737   } \r
6738   return hmenu;\r
6739 }\r
6740 \r
6741 WNDPROC consoleTextWindowProc;\r
6742 \r
6743 void\r
6744 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6745 {\r
6746   char buf[MSG_SIZ], name[MSG_SIZ];\r
6747   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6748   CHARRANGE sel;\r
6749 \r
6750   if (!getname) {\r
6751     SetWindowText(hInput, command);\r
6752     if (immediate) {\r
6753       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6754     } else {\r
6755       sel.cpMin = 999999;\r
6756       sel.cpMax = 999999;\r
6757       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6758       SetFocus(hInput);\r
6759     }\r
6760     return;\r
6761   }    \r
6762   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6763   if (sel.cpMin == sel.cpMax) {\r
6764     /* Expand to surrounding word */\r
6765     TEXTRANGE tr;\r
6766     do {\r
6767       tr.chrg.cpMax = sel.cpMin;\r
6768       tr.chrg.cpMin = --sel.cpMin;\r
6769       if (sel.cpMin < 0) break;\r
6770       tr.lpstrText = name;\r
6771       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6772     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6773     sel.cpMin++;\r
6774 \r
6775     do {\r
6776       tr.chrg.cpMin = sel.cpMax;\r
6777       tr.chrg.cpMax = ++sel.cpMax;\r
6778       tr.lpstrText = name;\r
6779       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6780     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6781     sel.cpMax--;\r
6782 \r
6783     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6784       MessageBeep(MB_ICONEXCLAMATION);\r
6785       return;\r
6786     }\r
6787     tr.chrg = sel;\r
6788     tr.lpstrText = name;\r
6789     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6790   } else {\r
6791     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6792       MessageBeep(MB_ICONEXCLAMATION);\r
6793       return;\r
6794     }\r
6795     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6796   }\r
6797   if (immediate) {\r
6798     sprintf(buf, "%s %s", command, name);\r
6799     SetWindowText(hInput, buf);\r
6800     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6801   } else {\r
6802     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6803     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
6804     SetWindowText(hInput, buf);\r
6805     sel.cpMin = 999999;\r
6806     sel.cpMax = 999999;\r
6807     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6808     SetFocus(hInput);\r
6809   }\r
6810 }\r
6811 \r
6812 LRESULT CALLBACK \r
6813 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6814 {\r
6815   HWND hInput;\r
6816   CHARRANGE sel;\r
6817 \r
6818   switch (message) {\r
6819   case WM_KEYDOWN:\r
6820     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6821     switch (wParam) {\r
6822     case VK_PRIOR:\r
6823       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6824       return 0;\r
6825     case VK_NEXT:\r
6826       sel.cpMin = 999999;\r
6827       sel.cpMax = 999999;\r
6828       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6829       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6830       return 0;\r
6831     }\r
6832     break;\r
6833   case WM_CHAR:\r
6834    if(wParam != '\022') {\r
6835     if (wParam == '\t') {\r
6836       if (GetKeyState(VK_SHIFT) < 0) {\r
6837         /* shifted */\r
6838         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6839         if (buttonDesc[0].hwnd) {\r
6840           SetFocus(buttonDesc[0].hwnd);\r
6841         } else {\r
6842           SetFocus(hwndMain);\r
6843         }\r
6844       } else {\r
6845         /* unshifted */\r
6846         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6847       }\r
6848     } else {\r
6849       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6850       JAWS_DELETE( SetFocus(hInput); )\r
6851       SendMessage(hInput, message, wParam, lParam);\r
6852     }\r
6853     return 0;\r
6854    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
6855   case WM_RBUTTONDOWN:\r
6856     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6857       /* Move selection here if it was empty */\r
6858       POINT pt;\r
6859       pt.x = LOWORD(lParam);\r
6860       pt.y = HIWORD(lParam);\r
6861       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6862       if (sel.cpMin == sel.cpMax) {\r
6863         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6864         sel.cpMax = sel.cpMin;\r
6865         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6866       }\r
6867       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6868 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6869       POINT pt;\r
6870       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6871       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6872       if (sel.cpMin == sel.cpMax) {\r
6873         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6874         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6875       }\r
6876       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6877         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6878       }\r
6879       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6880       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6881       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6882       MenuPopup(hwnd, pt, hmenu, -1);\r
6883 }\r
6884     }\r
6885     return 0;\r
6886   case WM_RBUTTONUP:\r
6887     if (GetKeyState(VK_SHIFT) & ~1) {\r
6888       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6889         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6890     }\r
6891     return 0;\r
6892   case WM_PASTE:\r
6893     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6894     SetFocus(hInput);\r
6895     return SendMessage(hInput, message, wParam, lParam);\r
6896   case WM_MBUTTONDOWN:\r
6897     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6898   case WM_COMMAND:\r
6899     switch (LOWORD(wParam)) {\r
6900     case IDM_QuickPaste:\r
6901       {\r
6902         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6903         if (sel.cpMin == sel.cpMax) {\r
6904           MessageBeep(MB_ICONEXCLAMATION);\r
6905           return 0;\r
6906         }\r
6907         SendMessage(hwnd, WM_COPY, 0, 0);\r
6908         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6909         SendMessage(hInput, WM_PASTE, 0, 0);\r
6910         SetFocus(hInput);\r
6911         return 0;\r
6912       }\r
6913     case IDM_Cut:\r
6914       SendMessage(hwnd, WM_CUT, 0, 0);\r
6915       return 0;\r
6916     case IDM_Paste:\r
6917       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6918       return 0;\r
6919     case IDM_Copy:\r
6920       SendMessage(hwnd, WM_COPY, 0, 0);\r
6921       return 0;\r
6922     default:\r
6923       {\r
6924         int i = LOWORD(wParam) - IDM_CommandX;\r
6925         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6926             icsTextMenuEntry[i].command != NULL) {\r
6927           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6928                    icsTextMenuEntry[i].getname,\r
6929                    icsTextMenuEntry[i].immediate);\r
6930           return 0;\r
6931         }\r
6932       }\r
6933       break;\r
6934     }\r
6935     break;\r
6936   }\r
6937   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
6938 }\r
6939 \r
6940 WNDPROC consoleInputWindowProc;\r
6941 \r
6942 LRESULT CALLBACK\r
6943 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6944 {\r
6945   char buf[MSG_SIZ];\r
6946   char *p;\r
6947   static BOOL sendNextChar = FALSE;\r
6948   static BOOL quoteNextChar = FALSE;\r
6949   InputSource *is = consoleInputSource;\r
6950   CHARFORMAT cf;\r
6951   CHARRANGE sel;\r
6952 \r
6953   switch (message) {\r
6954   case WM_CHAR:\r
6955     if (!appData.localLineEditing || sendNextChar) {\r
6956       is->buf[0] = (CHAR) wParam;\r
6957       is->count = 1;\r
6958       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6959       sendNextChar = FALSE;\r
6960       return 0;\r
6961     }\r
6962     if (quoteNextChar) {\r
6963       buf[0] = (char) wParam;\r
6964       buf[1] = NULLCHAR;\r
6965       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
6966       quoteNextChar = FALSE;\r
6967       return 0;\r
6968     }\r
6969     switch (wParam) {\r
6970     case '\r':   /* Enter key */\r
6971       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
6972       if (consoleEcho) SaveInHistory(is->buf);\r
6973       is->buf[is->count++] = '\n';\r
6974       is->buf[is->count] = NULLCHAR;\r
6975       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6976       if (consoleEcho) {\r
6977         ConsoleOutput(is->buf, is->count, TRUE);\r
6978       } else if (appData.localLineEditing) {\r
6979         ConsoleOutput("\n", 1, TRUE);\r
6980       }\r
6981       /* fall thru */\r
6982     case '\033': /* Escape key */\r
6983       SetWindowText(hwnd, "");\r
6984       cf.cbSize = sizeof(CHARFORMAT);\r
6985       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
6986       if (consoleEcho) {\r
6987         cf.crTextColor = textAttribs[ColorNormal].color;\r
6988       } else {\r
6989         cf.crTextColor = COLOR_ECHOOFF;\r
6990       }\r
6991       cf.dwEffects = textAttribs[ColorNormal].effects;\r
6992       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
6993       return 0;\r
6994     case '\t':   /* Tab key */\r
6995       if (GetKeyState(VK_SHIFT) < 0) {\r
6996         /* shifted */\r
6997         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
6998       } else {\r
6999         /* unshifted */\r
7000         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7001         if (buttonDesc[0].hwnd) {\r
7002           SetFocus(buttonDesc[0].hwnd);\r
7003         } else {\r
7004           SetFocus(hwndMain);\r
7005         }\r
7006       }\r
7007       return 0;\r
7008     case '\023': /* Ctrl+S */\r
7009       sendNextChar = TRUE;\r
7010       return 0;\r
7011     case '\021': /* Ctrl+Q */\r
7012       quoteNextChar = TRUE;\r
7013       return 0;\r
7014     JAWS_REPLAY\r
7015     default:\r
7016       break;\r
7017     }\r
7018     break;\r
7019   case WM_KEYDOWN:\r
7020     switch (wParam) {\r
7021     case VK_UP:\r
7022       GetWindowText(hwnd, buf, MSG_SIZ);\r
7023       p = PrevInHistory(buf);\r
7024       if (p != NULL) {\r
7025         SetWindowText(hwnd, p);\r
7026         sel.cpMin = 999999;\r
7027         sel.cpMax = 999999;\r
7028         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7029         return 0;\r
7030       }\r
7031       break;\r
7032     case VK_DOWN:\r
7033       p = NextInHistory();\r
7034       if (p != NULL) {\r
7035         SetWindowText(hwnd, p);\r
7036         sel.cpMin = 999999;\r
7037         sel.cpMax = 999999;\r
7038         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7039         return 0;\r
7040       }\r
7041       break;\r
7042     case VK_HOME:\r
7043     case VK_END:\r
7044       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7045       /* fall thru */\r
7046     case VK_PRIOR:\r
7047     case VK_NEXT:\r
7048       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7049       return 0;\r
7050     }\r
7051     break;\r
7052   case WM_MBUTTONDOWN:\r
7053     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7054       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7055     break;\r
7056   case WM_RBUTTONUP:\r
7057     if (GetKeyState(VK_SHIFT) & ~1) {\r
7058       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7059         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7060     } else {\r
7061       POINT pt;\r
7062       HMENU hmenu;\r
7063       hmenu = LoadMenu(hInst, "InputMenu");\r
7064       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7065       if (sel.cpMin == sel.cpMax) {\r
7066         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7067         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7068       }\r
7069       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7070         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7071       }\r
7072       pt.x = LOWORD(lParam);\r
7073       pt.y = HIWORD(lParam);\r
7074       MenuPopup(hwnd, pt, hmenu, -1);\r
7075     }\r
7076     return 0;\r
7077   case WM_COMMAND:\r
7078     switch (LOWORD(wParam)) { \r
7079     case IDM_Undo:\r
7080       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7081       return 0;\r
7082     case IDM_SelectAll:\r
7083       sel.cpMin = 0;\r
7084       sel.cpMax = -1; /*999999?*/\r
7085       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7086       return 0;\r
7087     case IDM_Cut:\r
7088       SendMessage(hwnd, WM_CUT, 0, 0);\r
7089       return 0;\r
7090     case IDM_Paste:\r
7091       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7092       return 0;\r
7093     case IDM_Copy:\r
7094       SendMessage(hwnd, WM_COPY, 0, 0);\r
7095       return 0;\r
7096     }\r
7097     break;\r
7098   }\r
7099   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7100 }\r
7101 \r
7102 #define CO_MAX  100000\r
7103 #define CO_TRIM   1000\r
7104 \r
7105 LRESULT CALLBACK\r
7106 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7107 {\r
7108   static SnapData sd;\r
7109   HWND hText, hInput;\r
7110   RECT rect;\r
7111   static int sizeX, sizeY;\r
7112   int newSizeX, newSizeY;\r
7113   MINMAXINFO *mmi;\r
7114   WORD wMask;\r
7115 \r
7116   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7117   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7118 \r
7119   switch (message) {\r
7120   case WM_NOTIFY:\r
7121     if (((NMHDR*)lParam)->code == EN_LINK)\r
7122     {\r
7123       ENLINK *pLink = (ENLINK*)lParam;\r
7124       if (pLink->msg == WM_LBUTTONUP)\r
7125       {\r
7126         TEXTRANGE tr;\r
7127 \r
7128         tr.chrg = pLink->chrg;\r
7129         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7130         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7131         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7132         free(tr.lpstrText);\r
7133       }\r
7134     }\r
7135     break;\r
7136   case WM_INITDIALOG: /* message: initialize dialog box */\r
7137     hwndConsole = hDlg;\r
7138     SetFocus(hInput);\r
7139     consoleTextWindowProc = (WNDPROC)\r
7140       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
7141     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7142     consoleInputWindowProc = (WNDPROC)\r
7143       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
7144     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7145     Colorize(ColorNormal, TRUE);\r
7146     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7147     ChangedConsoleFont();\r
7148     GetClientRect(hDlg, &rect);\r
7149     sizeX = rect.right;\r
7150     sizeY = rect.bottom;\r
7151     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7152         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7153       WINDOWPLACEMENT wp;\r
7154       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7155       wp.length = sizeof(WINDOWPLACEMENT);\r
7156       wp.flags = 0;\r
7157       wp.showCmd = SW_SHOW;\r
7158       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7159       wp.rcNormalPosition.left = wpConsole.x;\r
7160       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7161       wp.rcNormalPosition.top = wpConsole.y;\r
7162       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7163       SetWindowPlacement(hDlg, &wp);\r
7164     }\r
7165 \r
7166    // [HGM] Chessknight's change 2004-07-13\r
7167    else { /* Determine Defaults */\r
7168        WINDOWPLACEMENT wp;\r
7169        wpConsole.x = wpMain.width + 1;\r
7170        wpConsole.y = wpMain.y;\r
7171        wpConsole.width = screenWidth -  wpMain.width;\r
7172        wpConsole.height = wpMain.height;\r
7173        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7174        wp.length = sizeof(WINDOWPLACEMENT);\r
7175        wp.flags = 0;\r
7176        wp.showCmd = SW_SHOW;\r
7177        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7178        wp.rcNormalPosition.left = wpConsole.x;\r
7179        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7180        wp.rcNormalPosition.top = wpConsole.y;\r
7181        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7182        SetWindowPlacement(hDlg, &wp);\r
7183     }\r
7184 \r
7185    // Allow hText to highlight URLs and send notifications on them\r
7186    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7187    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7188    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7189    SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width\r
7190 \r
7191     return FALSE;\r
7192 \r
7193   case WM_SETFOCUS:\r
7194     SetFocus(hInput);\r
7195     return 0;\r
7196 \r
7197   case WM_CLOSE:\r
7198     ExitEvent(0);\r
7199     /* not reached */\r
7200     break;\r
7201 \r
7202   case WM_SIZE:\r
7203     if (IsIconic(hDlg)) break;\r
7204     newSizeX = LOWORD(lParam);\r
7205     newSizeY = HIWORD(lParam);\r
7206     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7207       RECT rectText, rectInput;\r
7208       POINT pt;\r
7209       int newTextHeight, newTextWidth;\r
7210       GetWindowRect(hText, &rectText);\r
7211       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7212       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7213       if (newTextHeight < 0) {\r
7214         newSizeY += -newTextHeight;\r
7215         newTextHeight = 0;\r
7216       }\r
7217       SetWindowPos(hText, NULL, 0, 0,\r
7218         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7219       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7220       pt.x = rectInput.left;\r
7221       pt.y = rectInput.top + newSizeY - sizeY;\r
7222       ScreenToClient(hDlg, &pt);\r
7223       SetWindowPos(hInput, NULL, \r
7224         pt.x, pt.y, /* needs client coords */   \r
7225         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7226         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7227     }\r
7228     sizeX = newSizeX;\r
7229     sizeY = newSizeY;\r
7230     break;\r
7231 \r
7232   case WM_GETMINMAXINFO:\r
7233     /* Prevent resizing window too small */\r
7234     mmi = (MINMAXINFO *) lParam;\r
7235     mmi->ptMinTrackSize.x = 100;\r
7236     mmi->ptMinTrackSize.y = 100;\r
7237     break;\r
7238 \r
7239   /* [AS] Snapping */\r
7240   case WM_ENTERSIZEMOVE:\r
7241     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7242 \r
7243   case WM_SIZING:\r
7244     return OnSizing( &sd, hDlg, wParam, lParam );\r
7245 \r
7246   case WM_MOVING:\r
7247     return OnMoving( &sd, hDlg, wParam, lParam );\r
7248 \r
7249   case WM_EXITSIZEMOVE:\r
7250         UpdateICSWidth(hText);\r
7251     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7252   }\r
7253 \r
7254   return DefWindowProc(hDlg, message, wParam, lParam);\r
7255 }\r
7256 \r
7257 \r
7258 VOID\r
7259 ConsoleCreate()\r
7260 {\r
7261   HWND hCons;\r
7262   if (hwndConsole) return;\r
7263   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7264   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7265 }\r
7266 \r
7267 \r
7268 VOID\r
7269 ConsoleOutput(char* data, int length, int forceVisible)\r
7270 {\r
7271   HWND hText;\r
7272   int trim, exlen;\r
7273   char *p, *q;\r
7274   char buf[CO_MAX+1];\r
7275   POINT pEnd;\r
7276   RECT rect;\r
7277   static int delayLF = 0;\r
7278   CHARRANGE savesel, sel;\r
7279 \r
7280   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7281   p = data;\r
7282   q = buf;\r
7283   if (delayLF) {\r
7284     *q++ = '\r';\r
7285     *q++ = '\n';\r
7286     delayLF = 0;\r
7287   }\r
7288   while (length--) {\r
7289     if (*p == '\n') {\r
7290       if (*++p) {\r
7291         *q++ = '\r';\r
7292         *q++ = '\n';\r
7293       } else {\r
7294         delayLF = 1;\r
7295       }\r
7296     } else if (*p == '\007') {\r
7297        MyPlaySound(&sounds[(int)SoundBell]);\r
7298        p++;\r
7299     } else {\r
7300       *q++ = *p++;\r
7301     }\r
7302   }\r
7303   *q = NULLCHAR;\r
7304   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7305   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7306   /* Save current selection */\r
7307   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7308   exlen = GetWindowTextLength(hText);\r
7309   /* Find out whether current end of text is visible */\r
7310   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7311   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7312   /* Trim existing text if it's too long */\r
7313   if (exlen + (q - buf) > CO_MAX) {\r
7314     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7315     sel.cpMin = 0;\r
7316     sel.cpMax = trim;\r
7317     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7318     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7319     exlen -= trim;\r
7320     savesel.cpMin -= trim;\r
7321     savesel.cpMax -= trim;\r
7322     if (exlen < 0) exlen = 0;\r
7323     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7324     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7325   }\r
7326   /* Append the new text */\r
7327   sel.cpMin = exlen;\r
7328   sel.cpMax = exlen;\r
7329   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7330   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7331   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7332   if (forceVisible || exlen == 0 ||\r
7333       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7334        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7335     /* Scroll to make new end of text visible if old end of text\r
7336        was visible or new text is an echo of user typein */\r
7337     sel.cpMin = 9999999;\r
7338     sel.cpMax = 9999999;\r
7339     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7340     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7341     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7342     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7343   }\r
7344   if (savesel.cpMax == exlen || forceVisible) {\r
7345     /* Move insert point to new end of text if it was at the old\r
7346        end of text or if the new text is an echo of user typein */\r
7347     sel.cpMin = 9999999;\r
7348     sel.cpMax = 9999999;\r
7349     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7350   } else {\r
7351     /* Restore previous selection */\r
7352     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7353   }\r
7354   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7355 }\r
7356 \r
7357 /*---------*/\r
7358 \r
7359 \r
7360 void\r
7361 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7362 {\r
7363   char buf[100];\r
7364   char *str;\r
7365   COLORREF oldFg, oldBg;\r
7366   HFONT oldFont;\r
7367   RECT rect;\r
7368 \r
7369   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
7370 \r
7371   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7372   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7373   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7374 \r
7375   rect.left = x;\r
7376   rect.right = x + squareSize;\r
7377   rect.top  = y;\r
7378   rect.bottom = y + squareSize;\r
7379   str = buf;\r
7380 \r
7381   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7382                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7383              y, ETO_CLIPPED|ETO_OPAQUE,\r
7384              &rect, str, strlen(str), NULL);\r
7385 \r
7386   (void) SetTextColor(hdc, oldFg);\r
7387   (void) SetBkColor(hdc, oldBg);\r
7388   (void) SelectObject(hdc, oldFont);\r
7389 }\r
7390 \r
7391 void\r
7392 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7393               RECT *rect, char *color, char *flagFell)\r
7394 {\r
7395   char buf[100];\r
7396   char *str;\r
7397   COLORREF oldFg, oldBg;\r
7398   HFONT oldFont;\r
7399 \r
7400   if (appData.clockMode) {\r
7401     if (tinyLayout)\r
7402       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7403     else\r
7404       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7405     str = buf;\r
7406   } else {\r
7407     str = color;\r
7408   }\r
7409 \r
7410   if (highlight) {\r
7411     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7412     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7413   } else {\r
7414     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7415     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7416   }\r
7417   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7418 \r
7419   JAWS_SILENCE\r
7420 \r
7421   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7422              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7423              rect, str, strlen(str), NULL);\r
7424   if(logoHeight > 0 && appData.clockMode) {\r
7425       RECT r;\r
7426       sprintf(buf, "%s %s", buf+7, flagFell);\r
7427       r.top = rect->top + logoHeight/2;\r
7428       r.left = rect->left;\r
7429       r.right = rect->right;\r
7430       r.bottom = rect->bottom;\r
7431       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7432                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7433                  &r, str, strlen(str), NULL);\r
7434   }\r
7435   (void) SetTextColor(hdc, oldFg);\r
7436   (void) SetBkColor(hdc, oldBg);\r
7437   (void) SelectObject(hdc, oldFont);\r
7438 }\r
7439 \r
7440 \r
7441 int\r
7442 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7443            OVERLAPPED *ovl)\r
7444 {\r
7445   int ok, err;\r
7446 \r
7447   /* [AS]  */\r
7448   if( count <= 0 ) {\r
7449     if (appData.debugMode) {\r
7450       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7451     }\r
7452 \r
7453     return ERROR_INVALID_USER_BUFFER;\r
7454   }\r
7455 \r
7456   ResetEvent(ovl->hEvent);\r
7457   ovl->Offset = ovl->OffsetHigh = 0;\r
7458   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7459   if (ok) {\r
7460     err = NO_ERROR;\r
7461   } else {\r
7462     err = GetLastError();\r
7463     if (err == ERROR_IO_PENDING) {\r
7464       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7465       if (ok)\r
7466         err = NO_ERROR;\r
7467       else\r
7468         err = GetLastError();\r
7469     }\r
7470   }\r
7471   return err;\r
7472 }\r
7473 \r
7474 int\r
7475 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7476             OVERLAPPED *ovl)\r
7477 {\r
7478   int ok, err;\r
7479 \r
7480   ResetEvent(ovl->hEvent);\r
7481   ovl->Offset = ovl->OffsetHigh = 0;\r
7482   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7483   if (ok) {\r
7484     err = NO_ERROR;\r
7485   } else {\r
7486     err = GetLastError();\r
7487     if (err == ERROR_IO_PENDING) {\r
7488       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7489       if (ok)\r
7490         err = NO_ERROR;\r
7491       else\r
7492         err = GetLastError();\r
7493     }\r
7494   }\r
7495   return err;\r
7496 }\r
7497 \r
7498 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7499 void CheckForInputBufferFull( InputSource * is )\r
7500 {\r
7501     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7502         /* Look for end of line */\r
7503         char * p = is->buf;\r
7504         \r
7505         while( p < is->next && *p != '\n' ) {\r
7506             p++;\r
7507         }\r
7508 \r
7509         if( p >= is->next ) {\r
7510             if (appData.debugMode) {\r
7511                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7512             }\r
7513 \r
7514             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7515             is->count = (DWORD) -1;\r
7516             is->next = is->buf;\r
7517         }\r
7518     }\r
7519 }\r
7520 \r
7521 DWORD\r
7522 InputThread(LPVOID arg)\r
7523 {\r
7524   InputSource *is;\r
7525   OVERLAPPED ovl;\r
7526 \r
7527   is = (InputSource *) arg;\r
7528   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7529   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7530   while (is->hThread != NULL) {\r
7531     is->error = DoReadFile(is->hFile, is->next,\r
7532                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7533                            &is->count, &ovl);\r
7534     if (is->error == NO_ERROR) {\r
7535       is->next += is->count;\r
7536     } else {\r
7537       if (is->error == ERROR_BROKEN_PIPE) {\r
7538         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7539         is->count = 0;\r
7540       } else {\r
7541         is->count = (DWORD) -1;\r
7542         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7543         break; \r
7544       }\r
7545     }\r
7546 \r
7547     CheckForInputBufferFull( is );\r
7548 \r
7549     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7550 \r
7551     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7552 \r
7553     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7554   }\r
7555 \r
7556   CloseHandle(ovl.hEvent);\r
7557   CloseHandle(is->hFile);\r
7558 \r
7559   if (appData.debugMode) {\r
7560     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7561   }\r
7562 \r
7563   return 0;\r
7564 }\r
7565 \r
7566 \r
7567 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7568 DWORD\r
7569 NonOvlInputThread(LPVOID arg)\r
7570 {\r
7571   InputSource *is;\r
7572   char *p, *q;\r
7573   int i;\r
7574   char prev;\r
7575 \r
7576   is = (InputSource *) arg;\r
7577   while (is->hThread != NULL) {\r
7578     is->error = ReadFile(is->hFile, is->next,\r
7579                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7580                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7581     if (is->error == NO_ERROR) {\r
7582       /* Change CRLF to LF */\r
7583       if (is->next > is->buf) {\r
7584         p = is->next - 1;\r
7585         i = is->count + 1;\r
7586       } else {\r
7587         p = is->next;\r
7588         i = is->count;\r
7589       }\r
7590       q = p;\r
7591       prev = NULLCHAR;\r
7592       while (i > 0) {\r
7593         if (prev == '\r' && *p == '\n') {\r
7594           *(q-1) = '\n';\r
7595           is->count--;\r
7596         } else { \r
7597           *q++ = *p;\r
7598         }\r
7599         prev = *p++;\r
7600         i--;\r
7601       }\r
7602       *q = NULLCHAR;\r
7603       is->next = q;\r
7604     } else {\r
7605       if (is->error == ERROR_BROKEN_PIPE) {\r
7606         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7607         is->count = 0; \r
7608       } else {\r
7609         is->count = (DWORD) -1;\r
7610       }\r
7611     }\r
7612 \r
7613     CheckForInputBufferFull( is );\r
7614 \r
7615     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7616 \r
7617     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7618 \r
7619     if (is->count < 0) break;  /* Quit on error */\r
7620   }\r
7621   CloseHandle(is->hFile);\r
7622   return 0;\r
7623 }\r
7624 \r
7625 DWORD\r
7626 SocketInputThread(LPVOID arg)\r
7627 {\r
7628   InputSource *is;\r
7629 \r
7630   is = (InputSource *) arg;\r
7631   while (is->hThread != NULL) {\r
7632     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7633     if ((int)is->count == SOCKET_ERROR) {\r
7634       is->count = (DWORD) -1;\r
7635       is->error = WSAGetLastError();\r
7636     } else {\r
7637       is->error = NO_ERROR;\r
7638       is->next += is->count;\r
7639       if (is->count == 0 && is->second == is) {\r
7640         /* End of file on stderr; quit with no message */\r
7641         break;\r
7642       }\r
7643     }\r
7644     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7645 \r
7646     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7647 \r
7648     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7649   }\r
7650   return 0;\r
7651 }\r
7652 \r
7653 VOID\r
7654 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7655 {\r
7656   InputSource *is;\r
7657 \r
7658   is = (InputSource *) lParam;\r
7659   if (is->lineByLine) {\r
7660     /* Feed in lines one by one */\r
7661     char *p = is->buf;\r
7662     char *q = p;\r
7663     while (q < is->next) {\r
7664       if (*q++ == '\n') {\r
7665         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7666         p = q;\r
7667       }\r
7668     }\r
7669     \r
7670     /* Move any partial line to the start of the buffer */\r
7671     q = is->buf;\r
7672     while (p < is->next) {\r
7673       *q++ = *p++;\r
7674     }\r
7675     is->next = q;\r
7676 \r
7677     if (is->error != NO_ERROR || is->count == 0) {\r
7678       /* Notify backend of the error.  Note: If there was a partial\r
7679          line at the end, it is not flushed through. */\r
7680       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7681     }\r
7682   } else {\r
7683     /* Feed in the whole chunk of input at once */\r
7684     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7685     is->next = is->buf;\r
7686   }\r
7687 }\r
7688 \r
7689 /*---------------------------------------------------------------------------*\\r
7690  *\r
7691  *  Menu enables. Used when setting various modes.\r
7692  *\r
7693 \*---------------------------------------------------------------------------*/\r
7694 \r
7695 typedef struct {\r
7696   int item;\r
7697   int flags;\r
7698 } Enables;\r
7699 \r
7700 VOID\r
7701 GreyRevert(Boolean grey)\r
7702 { // [HGM] vari: for retracting variations in local mode\r
7703   HMENU hmenu = GetMenu(hwndMain);\r
7704   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7705   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7706 }\r
7707 \r
7708 VOID\r
7709 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7710 {\r
7711   while (enab->item > 0) {\r
7712     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7713     enab++;\r
7714   }\r
7715 }\r
7716 \r
7717 Enables gnuEnables[] = {\r
7718   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7719   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7720   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7721   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7722   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7723   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7724   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7725   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7726   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7727   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7728   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7729   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7730   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7731   { -1, -1 }\r
7732 };\r
7733 \r
7734 Enables icsEnables[] = {\r
7735   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7736   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7737   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7738   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7739   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7740   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7741   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7742   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7743   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7744   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7745   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7746   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7747   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7748   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7749   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7750   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7751   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7752   { -1, -1 }\r
7753 };\r
7754 \r
7755 #if ZIPPY\r
7756 Enables zippyEnables[] = {\r
7757   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7758   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7759   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7760   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7761   { -1, -1 }\r
7762 };\r
7763 #endif\r
7764 \r
7765 Enables ncpEnables[] = {\r
7766   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7767   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7768   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7769   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7770   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7771   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7772   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7773   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7774   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7775   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7776   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7777   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7778   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7779   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7780   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7781   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7782   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7783   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7784   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7785   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7786   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7787   { -1, -1 }\r
7788 };\r
7789 \r
7790 Enables trainingOnEnables[] = {\r
7791   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7792   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7793   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7794   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7795   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7796   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7797   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7798   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7799   { -1, -1 }\r
7800 };\r
7801 \r
7802 Enables trainingOffEnables[] = {\r
7803   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7804   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7805   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7806   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7807   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7808   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7809   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7810   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7811   { -1, -1 }\r
7812 };\r
7813 \r
7814 /* These modify either ncpEnables or gnuEnables */\r
7815 Enables cmailEnables[] = {\r
7816   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7817   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7818   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7819   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7820   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7821   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7822   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7823   { -1, -1 }\r
7824 };\r
7825 \r
7826 Enables machineThinkingEnables[] = {\r
7827   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7828   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7829   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7830   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7831   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7832   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7833   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7834   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7835   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7836   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7837   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7838   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7839   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7840   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7841   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7842   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7843   { -1, -1 }\r
7844 };\r
7845 \r
7846 Enables userThinkingEnables[] = {\r
7847   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7848   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7849   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7850   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7851   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7852   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7853   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7854   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7855   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7856   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7857   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7858   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7859   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7860   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7861   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7862   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7863   { -1, -1 }\r
7864 };\r
7865 \r
7866 /*---------------------------------------------------------------------------*\\r
7867  *\r
7868  *  Front-end interface functions exported by XBoard.\r
7869  *  Functions appear in same order as prototypes in frontend.h.\r
7870  * \r
7871 \*---------------------------------------------------------------------------*/\r
7872 VOID\r
7873 ModeHighlight()\r
7874 {\r
7875   static UINT prevChecked = 0;\r
7876   static int prevPausing = 0;\r
7877   UINT nowChecked;\r
7878 \r
7879   if (pausing != prevPausing) {\r
7880     prevPausing = pausing;\r
7881     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7882                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7883     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7884   }\r
7885 \r
7886   switch (gameMode) {\r
7887   case BeginningOfGame:\r
7888     if (appData.icsActive)\r
7889       nowChecked = IDM_IcsClient;\r
7890     else if (appData.noChessProgram)\r
7891       nowChecked = IDM_EditGame;\r
7892     else\r
7893       nowChecked = IDM_MachineBlack;\r
7894     break;\r
7895   case MachinePlaysBlack:\r
7896     nowChecked = IDM_MachineBlack;\r
7897     break;\r
7898   case MachinePlaysWhite:\r
7899     nowChecked = IDM_MachineWhite;\r
7900     break;\r
7901   case TwoMachinesPlay:\r
7902     nowChecked = matchMode ? IDM_Match : IDM_TwoMachines; // [HGM] match\r
7903     break;\r
7904   case AnalyzeMode:\r
7905     nowChecked = IDM_AnalysisMode;\r
7906     break;\r
7907   case AnalyzeFile:\r
7908     nowChecked = IDM_AnalyzeFile;\r
7909     break;\r
7910   case EditGame:\r
7911     nowChecked = IDM_EditGame;\r
7912     break;\r
7913   case PlayFromGameFile:\r
7914     nowChecked = IDM_LoadGame;\r
7915     break;\r
7916   case EditPosition:\r
7917     nowChecked = IDM_EditPosition;\r
7918     break;\r
7919   case Training:\r
7920     nowChecked = IDM_Training;\r
7921     break;\r
7922   case IcsPlayingWhite:\r
7923   case IcsPlayingBlack:\r
7924   case IcsObserving:\r
7925   case IcsIdle:\r
7926     nowChecked = IDM_IcsClient;\r
7927     break;\r
7928   default:\r
7929   case EndOfGame:\r
7930     nowChecked = 0;\r
7931     break;\r
7932   }\r
7933   if (prevChecked != 0)\r
7934     (void) CheckMenuItem(GetMenu(hwndMain),\r
7935                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
7936   if (nowChecked != 0)\r
7937     (void) CheckMenuItem(GetMenu(hwndMain),\r
7938                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
7939 \r
7940   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
7941     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
7942                           MF_BYCOMMAND|MF_ENABLED);\r
7943   } else {\r
7944     (void) EnableMenuItem(GetMenu(hwndMain), \r
7945                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
7946   }\r
7947 \r
7948   prevChecked = nowChecked;\r
7949 \r
7950   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
7951   if (appData.icsActive) {\r
7952        if (appData.icsEngineAnalyze) {\r
7953                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7954                        MF_BYCOMMAND|MF_CHECKED);\r
7955        } else {\r
7956                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7957                        MF_BYCOMMAND|MF_UNCHECKED);\r
7958        }\r
7959   }\r
7960 }\r
7961 \r
7962 VOID\r
7963 SetICSMode()\r
7964 {\r
7965   HMENU hmenu = GetMenu(hwndMain);\r
7966   SetMenuEnables(hmenu, icsEnables);\r
7967   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
7968     MF_BYPOSITION|MF_ENABLED);\r
7969 #if ZIPPY\r
7970   if (appData.zippyPlay) {\r
7971     SetMenuEnables(hmenu, zippyEnables);\r
7972     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
7973          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
7974           MF_BYCOMMAND|MF_ENABLED);\r
7975   }\r
7976 #endif\r
7977 }\r
7978 \r
7979 VOID\r
7980 SetGNUMode()\r
7981 {\r
7982   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
7983 }\r
7984 \r
7985 VOID\r
7986 SetNCPMode()\r
7987 {\r
7988   HMENU hmenu = GetMenu(hwndMain);\r
7989   SetMenuEnables(hmenu, ncpEnables);\r
7990   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
7991     MF_BYPOSITION|MF_GRAYED);\r
7992     DrawMenuBar(hwndMain);\r
7993 }\r
7994 \r
7995 VOID\r
7996 SetCmailMode()\r
7997 {\r
7998   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
7999 }\r
8000 \r
8001 VOID \r
8002 SetTrainingModeOn()\r
8003 {\r
8004   int i;\r
8005   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8006   for (i = 0; i < N_BUTTONS; i++) {\r
8007     if (buttonDesc[i].hwnd != NULL)\r
8008       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8009   }\r
8010   CommentPopDown();\r
8011 }\r
8012 \r
8013 VOID SetTrainingModeOff()\r
8014 {\r
8015   int i;\r
8016   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8017   for (i = 0; i < N_BUTTONS; i++) {\r
8018     if (buttonDesc[i].hwnd != NULL)\r
8019       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8020   }\r
8021 }\r
8022 \r
8023 \r
8024 VOID\r
8025 SetUserThinkingEnables()\r
8026 {\r
8027   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8028 }\r
8029 \r
8030 VOID\r
8031 SetMachineThinkingEnables()\r
8032 {\r
8033   HMENU hMenu = GetMenu(hwndMain);\r
8034   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8035 \r
8036   SetMenuEnables(hMenu, machineThinkingEnables);\r
8037 \r
8038   if (gameMode == MachinePlaysBlack) {\r
8039     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8040   } else if (gameMode == MachinePlaysWhite) {\r
8041     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8042   } else if (gameMode == TwoMachinesPlay) {\r
8043     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8044   }\r
8045 }\r
8046 \r
8047 \r
8048 VOID\r
8049 DisplayTitle(char *str)\r
8050 {\r
8051   char title[MSG_SIZ], *host;\r
8052   if (str[0] != NULLCHAR) {\r
8053     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8054   } else if (appData.icsActive) {\r
8055     if (appData.icsCommPort[0] != NULLCHAR)\r
8056       host = "ICS";\r
8057     else \r
8058       host = appData.icsHost;\r
8059     sprintf(title, "%s: %s", szTitle, host);\r
8060   } else if (appData.noChessProgram) {\r
8061     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8062   } else {\r
8063     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8064     strcat(title, ": ");\r
8065     strcat(title, first.tidy);\r
8066   }\r
8067   SetWindowText(hwndMain, title);\r
8068 }\r
8069 \r
8070 \r
8071 VOID\r
8072 DisplayMessage(char *str1, char *str2)\r
8073 {\r
8074   HDC hdc;\r
8075   HFONT oldFont;\r
8076   int remain = MESSAGE_TEXT_MAX - 1;\r
8077   int len;\r
8078 \r
8079   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8080   messageText[0] = NULLCHAR;\r
8081   if (*str1) {\r
8082     len = strlen(str1);\r
8083     if (len > remain) len = remain;\r
8084     strncpy(messageText, str1, len);\r
8085     messageText[len] = NULLCHAR;\r
8086     remain -= len;\r
8087   }\r
8088   if (*str2 && remain >= 2) {\r
8089     if (*str1) {\r
8090       strcat(messageText, "  ");\r
8091       remain -= 2;\r
8092     }\r
8093     len = strlen(str2);\r
8094     if (len > remain) len = remain;\r
8095     strncat(messageText, str2, len);\r
8096   }\r
8097   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8098 \r
8099   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8100 \r
8101   SAYMACHINEMOVE();\r
8102 \r
8103   hdc = GetDC(hwndMain);\r
8104   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8105   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8106              &messageRect, messageText, strlen(messageText), NULL);\r
8107   (void) SelectObject(hdc, oldFont);\r
8108   (void) ReleaseDC(hwndMain, hdc);\r
8109 }\r
8110 \r
8111 VOID\r
8112 DisplayError(char *str, int error)\r
8113 {\r
8114   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8115   int len;\r
8116 \r
8117   if (error == 0) {\r
8118     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8119   } else {\r
8120     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8121                         NULL, error, LANG_NEUTRAL,\r
8122                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8123     if (len > 0) {\r
8124       sprintf(buf, "%s:\n%s", str, buf2);\r
8125     } else {\r
8126       ErrorMap *em = errmap;\r
8127       while (em->err != 0 && em->err != error) em++;\r
8128       if (em->err != 0) {\r
8129         sprintf(buf, "%s:\n%s", str, em->msg);\r
8130       } else {\r
8131         sprintf(buf, "%s:\nError code %d", str, error);\r
8132       }\r
8133     }\r
8134   }\r
8135   \r
8136   ErrorPopUp(_("Error"), buf);\r
8137 }\r
8138 \r
8139 \r
8140 VOID\r
8141 DisplayMoveError(char *str)\r
8142 {\r
8143   fromX = fromY = -1;\r
8144   ClearHighlights();\r
8145   DrawPosition(FALSE, NULL);\r
8146   if (appData.popupMoveErrors) {\r
8147     ErrorPopUp(_("Error"), str);\r
8148   } else {\r
8149     DisplayMessage(str, "");\r
8150     moveErrorMessageUp = TRUE;\r
8151   }\r
8152 }\r
8153 \r
8154 VOID\r
8155 DisplayFatalError(char *str, int error, int exitStatus)\r
8156 {\r
8157   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8158   int len;\r
8159   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8160 \r
8161   if (error != 0) {\r
8162     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8163                         NULL, error, LANG_NEUTRAL,\r
8164                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8165     if (len > 0) {\r
8166       sprintf(buf, "%s:\n%s", str, buf2);\r
8167     } else {\r
8168       ErrorMap *em = errmap;\r
8169       while (em->err != 0 && em->err != error) em++;\r
8170       if (em->err != 0) {\r
8171         sprintf(buf, "%s:\n%s", str, em->msg);\r
8172       } else {\r
8173         sprintf(buf, "%s:\nError code %d", str, error);\r
8174       }\r
8175     }\r
8176     str = buf;\r
8177   }\r
8178   if (appData.debugMode) {\r
8179     fprintf(debugFP, "%s: %s\n", label, str);\r
8180   }\r
8181   if (appData.popupExitMessage) {\r
8182     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8183                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8184   }\r
8185   ExitEvent(exitStatus);\r
8186 }\r
8187 \r
8188 \r
8189 VOID\r
8190 DisplayInformation(char *str)\r
8191 {\r
8192   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8193 }\r
8194 \r
8195 \r
8196 VOID\r
8197 DisplayNote(char *str)\r
8198 {\r
8199   ErrorPopUp(_("Note"), str);\r
8200 }\r
8201 \r
8202 \r
8203 typedef struct {\r
8204   char *title, *question, *replyPrefix;\r
8205   ProcRef pr;\r
8206 } QuestionParams;\r
8207 \r
8208 LRESULT CALLBACK\r
8209 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8210 {\r
8211   static QuestionParams *qp;\r
8212   char reply[MSG_SIZ];\r
8213   int len, err;\r
8214 \r
8215   switch (message) {\r
8216   case WM_INITDIALOG:\r
8217     qp = (QuestionParams *) lParam;\r
8218     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8219     Translate(hDlg, DLG_Question);\r
8220     SetWindowText(hDlg, qp->title);\r
8221     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8222     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8223     return FALSE;\r
8224 \r
8225   case WM_COMMAND:\r
8226     switch (LOWORD(wParam)) {\r
8227     case IDOK:\r
8228       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8229       if (*reply) strcat(reply, " ");\r
8230       len = strlen(reply);\r
8231       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8232       strcat(reply, "\n");\r
8233       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8234       EndDialog(hDlg, TRUE);\r
8235       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8236       return TRUE;\r
8237     case IDCANCEL:\r
8238       EndDialog(hDlg, FALSE);\r
8239       return TRUE;\r
8240     default:\r
8241       break;\r
8242     }\r
8243     break;\r
8244   }\r
8245   return FALSE;\r
8246 }\r
8247 \r
8248 VOID\r
8249 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8250 {\r
8251     QuestionParams qp;\r
8252     FARPROC lpProc;\r
8253     \r
8254     qp.title = title;\r
8255     qp.question = question;\r
8256     qp.replyPrefix = replyPrefix;\r
8257     qp.pr = pr;\r
8258     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8259     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8260       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8261     FreeProcInstance(lpProc);\r
8262 }\r
8263 \r
8264 /* [AS] Pick FRC position */\r
8265 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8266 {\r
8267     static int * lpIndexFRC;\r
8268     BOOL index_is_ok;\r
8269     char buf[16];\r
8270 \r
8271     switch( message )\r
8272     {\r
8273     case WM_INITDIALOG:\r
8274         lpIndexFRC = (int *) lParam;\r
8275 \r
8276         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8277         Translate(hDlg, DLG_NewGameFRC);\r
8278 \r
8279         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8280         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8281         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8282         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8283 \r
8284         break;\r
8285 \r
8286     case WM_COMMAND:\r
8287         switch( LOWORD(wParam) ) {\r
8288         case IDOK:\r
8289             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8290             EndDialog( hDlg, 0 );\r
8291             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8292             return TRUE;\r
8293         case IDCANCEL:\r
8294             EndDialog( hDlg, 1 );   \r
8295             return TRUE;\r
8296         case IDC_NFG_Edit:\r
8297             if( HIWORD(wParam) == EN_CHANGE ) {\r
8298                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8299 \r
8300                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8301             }\r
8302             return TRUE;\r
8303         case IDC_NFG_Random:\r
8304             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8305             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8306             return TRUE;\r
8307         }\r
8308 \r
8309         break;\r
8310     }\r
8311 \r
8312     return FALSE;\r
8313 }\r
8314 \r
8315 int NewGameFRC()\r
8316 {\r
8317     int result;\r
8318     int index = appData.defaultFrcPosition;\r
8319     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8320 \r
8321     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8322 \r
8323     if( result == 0 ) {\r
8324         appData.defaultFrcPosition = index;\r
8325     }\r
8326 \r
8327     return result;\r
8328 }\r
8329 \r
8330 /* [AS] Game list options. Refactored by HGM */\r
8331 \r
8332 HWND gameListOptionsDialog;\r
8333 \r
8334 // low-level front-end: clear text edit / list widget\r
8335 void\r
8336 GLT_ClearList()\r
8337 {\r
8338     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8339 }\r
8340 \r
8341 // low-level front-end: clear text edit / list widget\r
8342 void\r
8343 GLT_DeSelectList()\r
8344 {\r
8345     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8346 }\r
8347 \r
8348 // low-level front-end: append line to text edit / list widget\r
8349 void\r
8350 GLT_AddToList( char *name )\r
8351 {\r
8352     if( name != 0 ) {\r
8353             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8354     }\r
8355 }\r
8356 \r
8357 // low-level front-end: get line from text edit / list widget\r
8358 Boolean\r
8359 GLT_GetFromList( int index, char *name )\r
8360 {\r
8361     if( name != 0 ) {\r
8362             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8363                 return TRUE;\r
8364     }\r
8365     return FALSE;\r
8366 }\r
8367 \r
8368 void GLT_MoveSelection( HWND hDlg, int delta )\r
8369 {\r
8370     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8371     int idx2 = idx1 + delta;\r
8372     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8373 \r
8374     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8375         char buf[128];\r
8376 \r
8377         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8378         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8379         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8380         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8381     }\r
8382 }\r
8383 \r
8384 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8385 {\r
8386     switch( message )\r
8387     {\r
8388     case WM_INITDIALOG:\r
8389         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8390         \r
8391         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8392         Translate(hDlg, DLG_GameListOptions);\r
8393 \r
8394         /* Initialize list */\r
8395         GLT_TagsToList( lpUserGLT );\r
8396 \r
8397         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8398 \r
8399         break;\r
8400 \r
8401     case WM_COMMAND:\r
8402         switch( LOWORD(wParam) ) {\r
8403         case IDOK:\r
8404             GLT_ParseList();\r
8405             EndDialog( hDlg, 0 );\r
8406             return TRUE;\r
8407         case IDCANCEL:\r
8408             EndDialog( hDlg, 1 );\r
8409             return TRUE;\r
8410 \r
8411         case IDC_GLT_Default:\r
8412             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8413             return TRUE;\r
8414 \r
8415         case IDC_GLT_Restore:\r
8416             GLT_TagsToList( appData.gameListTags );\r
8417             return TRUE;\r
8418 \r
8419         case IDC_GLT_Up:\r
8420             GLT_MoveSelection( hDlg, -1 );\r
8421             return TRUE;\r
8422 \r
8423         case IDC_GLT_Down:\r
8424             GLT_MoveSelection( hDlg, +1 );\r
8425             return TRUE;\r
8426         }\r
8427 \r
8428         break;\r
8429     }\r
8430 \r
8431     return FALSE;\r
8432 }\r
8433 \r
8434 int GameListOptions()\r
8435 {\r
8436     int result;\r
8437     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8438 \r
8439       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8440 \r
8441     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8442 \r
8443     if( result == 0 ) {\r
8444         /* [AS] Memory leak here! */\r
8445         appData.gameListTags = strdup( lpUserGLT ); \r
8446     }\r
8447 \r
8448     return result;\r
8449 }\r
8450 \r
8451 VOID\r
8452 DisplayIcsInteractionTitle(char *str)\r
8453 {\r
8454   char consoleTitle[MSG_SIZ];\r
8455 \r
8456   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
8457   SetWindowText(hwndConsole, consoleTitle);\r
8458 }\r
8459 \r
8460 void\r
8461 DrawPosition(int fullRedraw, Board board)\r
8462 {\r
8463   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8464 }\r
8465 \r
8466 void NotifyFrontendLogin()\r
8467 {\r
8468         if (hwndConsole)\r
8469                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8470 }\r
8471 \r
8472 VOID\r
8473 ResetFrontEnd()\r
8474 {\r
8475   fromX = fromY = -1;\r
8476   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8477     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8478     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8479     dragInfo.lastpos = dragInfo.pos;\r
8480     dragInfo.start.x = dragInfo.start.y = -1;\r
8481     dragInfo.from = dragInfo.start;\r
8482     ReleaseCapture();\r
8483     DrawPosition(TRUE, NULL);\r
8484   }\r
8485   TagsPopDown();\r
8486 }\r
8487 \r
8488 \r
8489 VOID\r
8490 CommentPopUp(char *title, char *str)\r
8491 {\r
8492   HWND hwnd = GetActiveWindow();\r
8493   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8494   SAY(str);\r
8495   SetActiveWindow(hwnd);\r
8496 }\r
8497 \r
8498 VOID\r
8499 CommentPopDown(void)\r
8500 {\r
8501   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
8502   if (commentDialog) {\r
8503     ShowWindow(commentDialog, SW_HIDE);\r
8504   }\r
8505   commentUp = FALSE;\r
8506 }\r
8507 \r
8508 VOID\r
8509 EditCommentPopUp(int index, char *title, char *str)\r
8510 {\r
8511   EitherCommentPopUp(index, title, str, TRUE);\r
8512 }\r
8513 \r
8514 \r
8515 VOID\r
8516 RingBell()\r
8517 {\r
8518   MyPlaySound(&sounds[(int)SoundMove]);\r
8519 }\r
8520 \r
8521 VOID PlayIcsWinSound()\r
8522 {\r
8523   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8524 }\r
8525 \r
8526 VOID PlayIcsLossSound()\r
8527 {\r
8528   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8529 }\r
8530 \r
8531 VOID PlayIcsDrawSound()\r
8532 {\r
8533   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8534 }\r
8535 \r
8536 VOID PlayIcsUnfinishedSound()\r
8537 {\r
8538   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8539 }\r
8540 \r
8541 VOID\r
8542 PlayAlarmSound()\r
8543 {\r
8544   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8545 }\r
8546 \r
8547 \r
8548 VOID\r
8549 EchoOn()\r
8550 {\r
8551   HWND hInput;\r
8552   consoleEcho = TRUE;\r
8553   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8554   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8555   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8556 }\r
8557 \r
8558 \r
8559 VOID\r
8560 EchoOff()\r
8561 {\r
8562   CHARFORMAT cf;\r
8563   HWND hInput;\r
8564   consoleEcho = FALSE;\r
8565   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8566   /* This works OK: set text and background both to the same color */\r
8567   cf = consoleCF;\r
8568   cf.crTextColor = COLOR_ECHOOFF;\r
8569   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8570   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8571 }\r
8572 \r
8573 /* No Raw()...? */\r
8574 \r
8575 void Colorize(ColorClass cc, int continuation)\r
8576 {\r
8577   currentColorClass = cc;\r
8578   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8579   consoleCF.crTextColor = textAttribs[cc].color;\r
8580   consoleCF.dwEffects = textAttribs[cc].effects;\r
8581   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8582 }\r
8583 \r
8584 char *\r
8585 UserName()\r
8586 {\r
8587   static char buf[MSG_SIZ];\r
8588   DWORD bufsiz = MSG_SIZ;\r
8589 \r
8590   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8591         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8592   }\r
8593   if (!GetUserName(buf, &bufsiz)) {\r
8594     /*DisplayError("Error getting user name", GetLastError());*/\r
8595     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8596   }\r
8597   return buf;\r
8598 }\r
8599 \r
8600 char *\r
8601 HostName()\r
8602 {\r
8603   static char buf[MSG_SIZ];\r
8604   DWORD bufsiz = MSG_SIZ;\r
8605 \r
8606   if (!GetComputerName(buf, &bufsiz)) {\r
8607     /*DisplayError("Error getting host name", GetLastError());*/\r
8608     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8609   }\r
8610   return buf;\r
8611 }\r
8612 \r
8613 \r
8614 int\r
8615 ClockTimerRunning()\r
8616 {\r
8617   return clockTimerEvent != 0;\r
8618 }\r
8619 \r
8620 int\r
8621 StopClockTimer()\r
8622 {\r
8623   if (clockTimerEvent == 0) return FALSE;\r
8624   KillTimer(hwndMain, clockTimerEvent);\r
8625   clockTimerEvent = 0;\r
8626   return TRUE;\r
8627 }\r
8628 \r
8629 void\r
8630 StartClockTimer(long millisec)\r
8631 {\r
8632   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8633                              (UINT) millisec, NULL);\r
8634 }\r
8635 \r
8636 void\r
8637 DisplayWhiteClock(long timeRemaining, int highlight)\r
8638 {\r
8639   HDC hdc;\r
8640   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8641 \r
8642   if(appData.noGUI) return;\r
8643   hdc = GetDC(hwndMain);\r
8644   if (!IsIconic(hwndMain)) {\r
8645     DisplayAClock(hdc, timeRemaining, highlight, \r
8646                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8647   }\r
8648   if (highlight && iconCurrent == iconBlack) {\r
8649     iconCurrent = iconWhite;\r
8650     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8651     if (IsIconic(hwndMain)) {\r
8652       DrawIcon(hdc, 2, 2, iconCurrent);\r
8653     }\r
8654   }\r
8655   (void) ReleaseDC(hwndMain, hdc);\r
8656   if (hwndConsole)\r
8657     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8658 }\r
8659 \r
8660 void\r
8661 DisplayBlackClock(long timeRemaining, int highlight)\r
8662 {\r
8663   HDC hdc;\r
8664   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8665 \r
8666   if(appData.noGUI) return;\r
8667   hdc = GetDC(hwndMain);\r
8668   if (!IsIconic(hwndMain)) {\r
8669     DisplayAClock(hdc, timeRemaining, highlight, \r
8670                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8671   }\r
8672   if (highlight && iconCurrent == iconWhite) {\r
8673     iconCurrent = iconBlack;\r
8674     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8675     if (IsIconic(hwndMain)) {\r
8676       DrawIcon(hdc, 2, 2, iconCurrent);\r
8677     }\r
8678   }\r
8679   (void) ReleaseDC(hwndMain, hdc);\r
8680   if (hwndConsole)\r
8681     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8682 }\r
8683 \r
8684 \r
8685 int\r
8686 LoadGameTimerRunning()\r
8687 {\r
8688   return loadGameTimerEvent != 0;\r
8689 }\r
8690 \r
8691 int\r
8692 StopLoadGameTimer()\r
8693 {\r
8694   if (loadGameTimerEvent == 0) return FALSE;\r
8695   KillTimer(hwndMain, loadGameTimerEvent);\r
8696   loadGameTimerEvent = 0;\r
8697   return TRUE;\r
8698 }\r
8699 \r
8700 void\r
8701 StartLoadGameTimer(long millisec)\r
8702 {\r
8703   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8704                                 (UINT) millisec, NULL);\r
8705 }\r
8706 \r
8707 void\r
8708 AutoSaveGame()\r
8709 {\r
8710   char *defName;\r
8711   FILE *f;\r
8712   char fileTitle[MSG_SIZ];\r
8713 \r
8714   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8715   f = OpenFileDialog(hwndMain, "a", defName,\r
8716                      appData.oldSaveStyle ? "gam" : "pgn",\r
8717                      GAME_FILT, \r
8718                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8719   if (f != NULL) {\r
8720     SaveGame(f, 0, "");\r
8721     fclose(f);\r
8722   }\r
8723 }\r
8724 \r
8725 \r
8726 void\r
8727 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8728 {\r
8729   if (delayedTimerEvent != 0) {\r
8730     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8731       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8732     }\r
8733     KillTimer(hwndMain, delayedTimerEvent);\r
8734     delayedTimerEvent = 0;\r
8735     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8736     delayedTimerCallback();\r
8737   }\r
8738   delayedTimerCallback = cb;\r
8739   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8740                                 (UINT) millisec, NULL);\r
8741 }\r
8742 \r
8743 DelayedEventCallback\r
8744 GetDelayedEvent()\r
8745 {\r
8746   if (delayedTimerEvent) {\r
8747     return delayedTimerCallback;\r
8748   } else {\r
8749     return NULL;\r
8750   }\r
8751 }\r
8752 \r
8753 void\r
8754 CancelDelayedEvent()\r
8755 {\r
8756   if (delayedTimerEvent) {\r
8757     KillTimer(hwndMain, delayedTimerEvent);\r
8758     delayedTimerEvent = 0;\r
8759   }\r
8760 }\r
8761 \r
8762 DWORD GetWin32Priority(int nice)\r
8763 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8764 /*\r
8765 REALTIME_PRIORITY_CLASS     0x00000100\r
8766 HIGH_PRIORITY_CLASS         0x00000080\r
8767 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8768 NORMAL_PRIORITY_CLASS       0x00000020\r
8769 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8770 IDLE_PRIORITY_CLASS         0x00000040\r
8771 */\r
8772         if (nice < -15) return 0x00000080;\r
8773         if (nice < 0)   return 0x00008000;\r
8774         if (nice == 0)  return 0x00000020;\r
8775         if (nice < 15)  return 0x00004000;\r
8776         return 0x00000040;\r
8777 }\r
8778 \r
8779 /* Start a child process running the given program.\r
8780    The process's standard output can be read from "from", and its\r
8781    standard input can be written to "to".\r
8782    Exit with fatal error if anything goes wrong.\r
8783    Returns an opaque pointer that can be used to destroy the process\r
8784    later.\r
8785 */\r
8786 int\r
8787 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8788 {\r
8789 #define BUFSIZE 4096\r
8790 \r
8791   HANDLE hChildStdinRd, hChildStdinWr,\r
8792     hChildStdoutRd, hChildStdoutWr;\r
8793   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8794   SECURITY_ATTRIBUTES saAttr;\r
8795   BOOL fSuccess;\r
8796   PROCESS_INFORMATION piProcInfo;\r
8797   STARTUPINFO siStartInfo;\r
8798   ChildProc *cp;\r
8799   char buf[MSG_SIZ];\r
8800   DWORD err;\r
8801 \r
8802   if (appData.debugMode) {\r
8803     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8804   }\r
8805 \r
8806   *pr = NoProc;\r
8807 \r
8808   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8809   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8810   saAttr.bInheritHandle = TRUE;\r
8811   saAttr.lpSecurityDescriptor = NULL;\r
8812 \r
8813   /*\r
8814    * The steps for redirecting child's STDOUT:\r
8815    *     1. Create anonymous pipe to be STDOUT for child.\r
8816    *     2. Create a noninheritable duplicate of read handle,\r
8817    *         and close the inheritable read handle.\r
8818    */\r
8819 \r
8820   /* Create a pipe for the child's STDOUT. */\r
8821   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8822     return GetLastError();\r
8823   }\r
8824 \r
8825   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8826   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8827                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8828                              FALSE,     /* not inherited */\r
8829                              DUPLICATE_SAME_ACCESS);\r
8830   if (! fSuccess) {\r
8831     return GetLastError();\r
8832   }\r
8833   CloseHandle(hChildStdoutRd);\r
8834 \r
8835   /*\r
8836    * The steps for redirecting child's STDIN:\r
8837    *     1. Create anonymous pipe to be STDIN for child.\r
8838    *     2. Create a noninheritable duplicate of write handle,\r
8839    *         and close the inheritable write handle.\r
8840    */\r
8841 \r
8842   /* Create a pipe for the child's STDIN. */\r
8843   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8844     return GetLastError();\r
8845   }\r
8846 \r
8847   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8848   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8849                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8850                              FALSE,     /* not inherited */\r
8851                              DUPLICATE_SAME_ACCESS);\r
8852   if (! fSuccess) {\r
8853     return GetLastError();\r
8854   }\r
8855   CloseHandle(hChildStdinWr);\r
8856 \r
8857   /* Arrange to (1) look in dir for the child .exe file, and\r
8858    * (2) have dir be the child's working directory.  Interpret\r
8859    * dir relative to the directory WinBoard loaded from. */\r
8860   GetCurrentDirectory(MSG_SIZ, buf);\r
8861   SetCurrentDirectory(installDir);\r
8862   SetCurrentDirectory(dir);\r
8863 \r
8864   /* Now create the child process. */\r
8865 \r
8866   siStartInfo.cb = sizeof(STARTUPINFO);\r
8867   siStartInfo.lpReserved = NULL;\r
8868   siStartInfo.lpDesktop = NULL;\r
8869   siStartInfo.lpTitle = NULL;\r
8870   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8871   siStartInfo.cbReserved2 = 0;\r
8872   siStartInfo.lpReserved2 = NULL;\r
8873   siStartInfo.hStdInput = hChildStdinRd;\r
8874   siStartInfo.hStdOutput = hChildStdoutWr;\r
8875   siStartInfo.hStdError = hChildStdoutWr;\r
8876 \r
8877   fSuccess = CreateProcess(NULL,\r
8878                            cmdLine,        /* command line */\r
8879                            NULL,           /* process security attributes */\r
8880                            NULL,           /* primary thread security attrs */\r
8881                            TRUE,           /* handles are inherited */\r
8882                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8883                            NULL,           /* use parent's environment */\r
8884                            NULL,\r
8885                            &siStartInfo, /* STARTUPINFO pointer */\r
8886                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
8887 \r
8888   err = GetLastError();\r
8889   SetCurrentDirectory(buf); /* return to prev directory */\r
8890   if (! fSuccess) {\r
8891     return err;\r
8892   }\r
8893 \r
8894   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
8895     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
8896     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
8897   }\r
8898 \r
8899   /* Close the handles we don't need in the parent */\r
8900   CloseHandle(piProcInfo.hThread);\r
8901   CloseHandle(hChildStdinRd);\r
8902   CloseHandle(hChildStdoutWr);\r
8903 \r
8904   /* Prepare return value */\r
8905   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8906   cp->kind = CPReal;\r
8907   cp->hProcess = piProcInfo.hProcess;\r
8908   cp->pid = piProcInfo.dwProcessId;\r
8909   cp->hFrom = hChildStdoutRdDup;\r
8910   cp->hTo = hChildStdinWrDup;\r
8911 \r
8912   *pr = (void *) cp;\r
8913 \r
8914   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
8915      2000 where engines sometimes don't see the initial command(s)\r
8916      from WinBoard and hang.  I don't understand how that can happen,\r
8917      but the Sleep is harmless, so I've put it in.  Others have also\r
8918      reported what may be the same problem, so hopefully this will fix\r
8919      it for them too.  */\r
8920   Sleep(500);\r
8921 \r
8922   return NO_ERROR;\r
8923 }\r
8924 \r
8925 \r
8926 void\r
8927 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
8928 {\r
8929   ChildProc *cp; int result;\r
8930 \r
8931   cp = (ChildProc *) pr;\r
8932   if (cp == NULL) return;\r
8933 \r
8934   switch (cp->kind) {\r
8935   case CPReal:\r
8936     /* TerminateProcess is considered harmful, so... */\r
8937     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
8938     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
8939     /* The following doesn't work because the chess program\r
8940        doesn't "have the same console" as WinBoard.  Maybe\r
8941        we could arrange for this even though neither WinBoard\r
8942        nor the chess program uses a console for stdio? */\r
8943     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
8944 \r
8945     /* [AS] Special termination modes for misbehaving programs... */\r
8946     if( signal == 9 ) { \r
8947         result = TerminateProcess( cp->hProcess, 0 );\r
8948 \r
8949         if ( appData.debugMode) {\r
8950             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
8951         }\r
8952     }\r
8953     else if( signal == 10 ) {\r
8954         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
8955 \r
8956         if( dw != WAIT_OBJECT_0 ) {\r
8957             result = TerminateProcess( cp->hProcess, 0 );\r
8958 \r
8959             if ( appData.debugMode) {\r
8960                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
8961             }\r
8962 \r
8963         }\r
8964     }\r
8965 \r
8966     CloseHandle(cp->hProcess);\r
8967     break;\r
8968 \r
8969   case CPComm:\r
8970     if (cp->hFrom) CloseHandle(cp->hFrom);\r
8971     break;\r
8972 \r
8973   case CPSock:\r
8974     closesocket(cp->sock);\r
8975     WSACleanup();\r
8976     break;\r
8977 \r
8978   case CPRcmd:\r
8979     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
8980     closesocket(cp->sock);\r
8981     closesocket(cp->sock2);\r
8982     WSACleanup();\r
8983     break;\r
8984   }\r
8985   free(cp);\r
8986 }\r
8987 \r
8988 void\r
8989 InterruptChildProcess(ProcRef pr)\r
8990 {\r
8991   ChildProc *cp;\r
8992 \r
8993   cp = (ChildProc *) pr;\r
8994   if (cp == NULL) return;\r
8995   switch (cp->kind) {\r
8996   case CPReal:\r
8997     /* The following doesn't work because the chess program\r
8998        doesn't "have the same console" as WinBoard.  Maybe\r
8999        we could arrange for this even though neither WinBoard\r
9000        nor the chess program uses a console for stdio */\r
9001     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9002     break;\r
9003 \r
9004   case CPComm:\r
9005   case CPSock:\r
9006     /* Can't interrupt */\r
9007     break;\r
9008 \r
9009   case CPRcmd:\r
9010     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9011     break;\r
9012   }\r
9013 }\r
9014 \r
9015 \r
9016 int\r
9017 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9018 {\r
9019   char cmdLine[MSG_SIZ];\r
9020 \r
9021   if (port[0] == NULLCHAR) {\r
9022     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
9023   } else {\r
9024     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
9025   }\r
9026   return StartChildProcess(cmdLine, "", pr);\r
9027 }\r
9028 \r
9029 \r
9030 /* Code to open TCP sockets */\r
9031 \r
9032 int\r
9033 OpenTCP(char *host, char *port, ProcRef *pr)\r
9034 {\r
9035   ChildProc *cp;\r
9036   int err;\r
9037   SOCKET s;\r
9038   struct sockaddr_in sa, mysa;\r
9039   struct hostent FAR *hp;\r
9040   unsigned short uport;\r
9041   WORD wVersionRequested;\r
9042   WSADATA wsaData;\r
9043 \r
9044   /* Initialize socket DLL */\r
9045   wVersionRequested = MAKEWORD(1, 1);\r
9046   err = WSAStartup(wVersionRequested, &wsaData);\r
9047   if (err != 0) return err;\r
9048 \r
9049   /* Make socket */\r
9050   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9051     err = WSAGetLastError();\r
9052     WSACleanup();\r
9053     return err;\r
9054   }\r
9055 \r
9056   /* Bind local address using (mostly) don't-care values.\r
9057    */\r
9058   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9059   mysa.sin_family = AF_INET;\r
9060   mysa.sin_addr.s_addr = INADDR_ANY;\r
9061   uport = (unsigned short) 0;\r
9062   mysa.sin_port = htons(uport);\r
9063   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9064       == SOCKET_ERROR) {\r
9065     err = WSAGetLastError();\r
9066     WSACleanup();\r
9067     return err;\r
9068   }\r
9069 \r
9070   /* Resolve remote host name */\r
9071   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9072   if (!(hp = gethostbyname(host))) {\r
9073     unsigned int b0, b1, b2, b3;\r
9074 \r
9075     err = WSAGetLastError();\r
9076 \r
9077     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9078       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9079       hp->h_addrtype = AF_INET;\r
9080       hp->h_length = 4;\r
9081       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9082       hp->h_addr_list[0] = (char *) malloc(4);\r
9083       hp->h_addr_list[0][0] = (char) b0;\r
9084       hp->h_addr_list[0][1] = (char) b1;\r
9085       hp->h_addr_list[0][2] = (char) b2;\r
9086       hp->h_addr_list[0][3] = (char) b3;\r
9087     } else {\r
9088       WSACleanup();\r
9089       return err;\r
9090     }\r
9091   }\r
9092   sa.sin_family = hp->h_addrtype;\r
9093   uport = (unsigned short) atoi(port);\r
9094   sa.sin_port = htons(uport);\r
9095   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9096 \r
9097   /* Make connection */\r
9098   if (connect(s, (struct sockaddr *) &sa,\r
9099               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9100     err = WSAGetLastError();\r
9101     WSACleanup();\r
9102     return err;\r
9103   }\r
9104 \r
9105   /* Prepare return value */\r
9106   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9107   cp->kind = CPSock;\r
9108   cp->sock = s;\r
9109   *pr = (ProcRef *) cp;\r
9110 \r
9111   return NO_ERROR;\r
9112 }\r
9113 \r
9114 int\r
9115 OpenCommPort(char *name, ProcRef *pr)\r
9116 {\r
9117   HANDLE h;\r
9118   COMMTIMEOUTS ct;\r
9119   ChildProc *cp;\r
9120   char fullname[MSG_SIZ];\r
9121 \r
9122   if (*name != '\\')\r
9123     sprintf(fullname, "\\\\.\\%s", name);\r
9124   else\r
9125     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9126 \r
9127   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9128                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9129   if (h == (HANDLE) -1) {\r
9130     return GetLastError();\r
9131   }\r
9132   hCommPort = h;\r
9133 \r
9134   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9135 \r
9136   /* Accumulate characters until a 100ms pause, then parse */\r
9137   ct.ReadIntervalTimeout = 100;\r
9138   ct.ReadTotalTimeoutMultiplier = 0;\r
9139   ct.ReadTotalTimeoutConstant = 0;\r
9140   ct.WriteTotalTimeoutMultiplier = 0;\r
9141   ct.WriteTotalTimeoutConstant = 0;\r
9142   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9143 \r
9144   /* Prepare return value */\r
9145   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9146   cp->kind = CPComm;\r
9147   cp->hFrom = h;\r
9148   cp->hTo = h;\r
9149   *pr = (ProcRef *) cp;\r
9150 \r
9151   return NO_ERROR;\r
9152 }\r
9153 \r
9154 int\r
9155 OpenLoopback(ProcRef *pr)\r
9156 {\r
9157   DisplayFatalError(_("Not implemented"), 0, 1);\r
9158   return NO_ERROR;\r
9159 }\r
9160 \r
9161 \r
9162 int\r
9163 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9164 {\r
9165   ChildProc *cp;\r
9166   int err;\r
9167   SOCKET s, s2, s3;\r
9168   struct sockaddr_in sa, mysa;\r
9169   struct hostent FAR *hp;\r
9170   unsigned short uport;\r
9171   WORD wVersionRequested;\r
9172   WSADATA wsaData;\r
9173   int fromPort;\r
9174   char stderrPortStr[MSG_SIZ];\r
9175 \r
9176   /* Initialize socket DLL */\r
9177   wVersionRequested = MAKEWORD(1, 1);\r
9178   err = WSAStartup(wVersionRequested, &wsaData);\r
9179   if (err != 0) return err;\r
9180 \r
9181   /* Resolve remote host name */\r
9182   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9183   if (!(hp = gethostbyname(host))) {\r
9184     unsigned int b0, b1, b2, b3;\r
9185 \r
9186     err = WSAGetLastError();\r
9187 \r
9188     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9189       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9190       hp->h_addrtype = AF_INET;\r
9191       hp->h_length = 4;\r
9192       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9193       hp->h_addr_list[0] = (char *) malloc(4);\r
9194       hp->h_addr_list[0][0] = (char) b0;\r
9195       hp->h_addr_list[0][1] = (char) b1;\r
9196       hp->h_addr_list[0][2] = (char) b2;\r
9197       hp->h_addr_list[0][3] = (char) b3;\r
9198     } else {\r
9199       WSACleanup();\r
9200       return err;\r
9201     }\r
9202   }\r
9203   sa.sin_family = hp->h_addrtype;\r
9204   uport = (unsigned short) 514;\r
9205   sa.sin_port = htons(uport);\r
9206   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9207 \r
9208   /* Bind local socket to unused "privileged" port address\r
9209    */\r
9210   s = INVALID_SOCKET;\r
9211   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9212   mysa.sin_family = AF_INET;\r
9213   mysa.sin_addr.s_addr = INADDR_ANY;\r
9214   for (fromPort = 1023;; fromPort--) {\r
9215     if (fromPort < 0) {\r
9216       WSACleanup();\r
9217       return WSAEADDRINUSE;\r
9218     }\r
9219     if (s == INVALID_SOCKET) {\r
9220       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9221         err = WSAGetLastError();\r
9222         WSACleanup();\r
9223         return err;\r
9224       }\r
9225     }\r
9226     uport = (unsigned short) fromPort;\r
9227     mysa.sin_port = htons(uport);\r
9228     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9229         == SOCKET_ERROR) {\r
9230       err = WSAGetLastError();\r
9231       if (err == WSAEADDRINUSE) continue;\r
9232       WSACleanup();\r
9233       return err;\r
9234     }\r
9235     if (connect(s, (struct sockaddr *) &sa,\r
9236       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9237       err = WSAGetLastError();\r
9238       if (err == WSAEADDRINUSE) {\r
9239         closesocket(s);\r
9240         s = -1;\r
9241         continue;\r
9242       }\r
9243       WSACleanup();\r
9244       return err;\r
9245     }\r
9246     break;\r
9247   }\r
9248 \r
9249   /* Bind stderr local socket to unused "privileged" port address\r
9250    */\r
9251   s2 = INVALID_SOCKET;\r
9252   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9253   mysa.sin_family = AF_INET;\r
9254   mysa.sin_addr.s_addr = INADDR_ANY;\r
9255   for (fromPort = 1023;; fromPort--) {\r
9256     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9257     if (fromPort < 0) {\r
9258       (void) closesocket(s);\r
9259       WSACleanup();\r
9260       return WSAEADDRINUSE;\r
9261     }\r
9262     if (s2 == INVALID_SOCKET) {\r
9263       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9264         err = WSAGetLastError();\r
9265         closesocket(s);\r
9266         WSACleanup();\r
9267         return err;\r
9268       }\r
9269     }\r
9270     uport = (unsigned short) fromPort;\r
9271     mysa.sin_port = htons(uport);\r
9272     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9273         == SOCKET_ERROR) {\r
9274       err = WSAGetLastError();\r
9275       if (err == WSAEADDRINUSE) continue;\r
9276       (void) closesocket(s);\r
9277       WSACleanup();\r
9278       return err;\r
9279     }\r
9280     if (listen(s2, 1) == SOCKET_ERROR) {\r
9281       err = WSAGetLastError();\r
9282       if (err == WSAEADDRINUSE) {\r
9283         closesocket(s2);\r
9284         s2 = INVALID_SOCKET;\r
9285         continue;\r
9286       }\r
9287       (void) closesocket(s);\r
9288       (void) closesocket(s2);\r
9289       WSACleanup();\r
9290       return err;\r
9291     }\r
9292     break;\r
9293   }\r
9294   prevStderrPort = fromPort; // remember port used\r
9295   sprintf(stderrPortStr, "%d", fromPort);\r
9296 \r
9297   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9298     err = WSAGetLastError();\r
9299     (void) closesocket(s);\r
9300     (void) closesocket(s2);\r
9301     WSACleanup();\r
9302     return err;\r
9303   }\r
9304 \r
9305   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9306     err = WSAGetLastError();\r
9307     (void) closesocket(s);\r
9308     (void) closesocket(s2);\r
9309     WSACleanup();\r
9310     return err;\r
9311   }\r
9312   if (*user == NULLCHAR) user = UserName();\r
9313   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9314     err = WSAGetLastError();\r
9315     (void) closesocket(s);\r
9316     (void) closesocket(s2);\r
9317     WSACleanup();\r
9318     return err;\r
9319   }\r
9320   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9321     err = WSAGetLastError();\r
9322     (void) closesocket(s);\r
9323     (void) closesocket(s2);\r
9324     WSACleanup();\r
9325     return err;\r
9326   }\r
9327 \r
9328   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9329     err = WSAGetLastError();\r
9330     (void) closesocket(s);\r
9331     (void) closesocket(s2);\r
9332     WSACleanup();\r
9333     return err;\r
9334   }\r
9335   (void) closesocket(s2);  /* Stop listening */\r
9336 \r
9337   /* Prepare return value */\r
9338   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9339   cp->kind = CPRcmd;\r
9340   cp->sock = s;\r
9341   cp->sock2 = s3;\r
9342   *pr = (ProcRef *) cp;\r
9343 \r
9344   return NO_ERROR;\r
9345 }\r
9346 \r
9347 \r
9348 InputSourceRef\r
9349 AddInputSource(ProcRef pr, int lineByLine,\r
9350                InputCallback func, VOIDSTAR closure)\r
9351 {\r
9352   InputSource *is, *is2 = NULL;\r
9353   ChildProc *cp = (ChildProc *) pr;\r
9354 \r
9355   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9356   is->lineByLine = lineByLine;\r
9357   is->func = func;\r
9358   is->closure = closure;\r
9359   is->second = NULL;\r
9360   is->next = is->buf;\r
9361   if (pr == NoProc) {\r
9362     is->kind = CPReal;\r
9363     consoleInputSource = is;\r
9364   } else {\r
9365     is->kind = cp->kind;\r
9366     /* \r
9367         [AS] Try to avoid a race condition if the thread is given control too early:\r
9368         we create all threads suspended so that the is->hThread variable can be\r
9369         safely assigned, then let the threads start with ResumeThread.\r
9370     */\r
9371     switch (cp->kind) {\r
9372     case CPReal:\r
9373       is->hFile = cp->hFrom;\r
9374       cp->hFrom = NULL; /* now owned by InputThread */\r
9375       is->hThread =\r
9376         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9377                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9378       break;\r
9379 \r
9380     case CPComm:\r
9381       is->hFile = cp->hFrom;\r
9382       cp->hFrom = NULL; /* now owned by InputThread */\r
9383       is->hThread =\r
9384         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9385                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9386       break;\r
9387 \r
9388     case CPSock:\r
9389       is->sock = cp->sock;\r
9390       is->hThread =\r
9391         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9392                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9393       break;\r
9394 \r
9395     case CPRcmd:\r
9396       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9397       *is2 = *is;\r
9398       is->sock = cp->sock;\r
9399       is->second = is2;\r
9400       is2->sock = cp->sock2;\r
9401       is2->second = is2;\r
9402       is->hThread =\r
9403         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9404                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9405       is2->hThread =\r
9406         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9407                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9408       break;\r
9409     }\r
9410 \r
9411     if( is->hThread != NULL ) {\r
9412         ResumeThread( is->hThread );\r
9413     }\r
9414 \r
9415     if( is2 != NULL && is2->hThread != NULL ) {\r
9416         ResumeThread( is2->hThread );\r
9417     }\r
9418   }\r
9419 \r
9420   return (InputSourceRef) is;\r
9421 }\r
9422 \r
9423 void\r
9424 RemoveInputSource(InputSourceRef isr)\r
9425 {\r
9426   InputSource *is;\r
9427 \r
9428   is = (InputSource *) isr;\r
9429   is->hThread = NULL;  /* tell thread to stop */\r
9430   CloseHandle(is->hThread);\r
9431   if (is->second != NULL) {\r
9432     is->second->hThread = NULL;\r
9433     CloseHandle(is->second->hThread);\r
9434   }\r
9435 }\r
9436 \r
9437 int no_wrap(char *message, int count)\r
9438 {\r
9439     ConsoleOutput(message, count, FALSE);\r
9440     return count;\r
9441 }\r
9442 \r
9443 int\r
9444 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9445 {\r
9446   DWORD dOutCount;\r
9447   int outCount = SOCKET_ERROR;\r
9448   ChildProc *cp = (ChildProc *) pr;\r
9449   static OVERLAPPED ovl;\r
9450   static int line = 0;\r
9451 \r
9452   if (pr == NoProc)\r
9453   {\r
9454     if (appData.noJoin || !appData.useInternalWrap)\r
9455       return no_wrap(message, count);\r
9456     else\r
9457     {\r
9458       int width = get_term_width();\r
9459       int len = wrap(NULL, message, count, width, &line);\r
9460       char *msg = malloc(len);\r
9461       int dbgchk;\r
9462 \r
9463       if (!msg)\r
9464         return no_wrap(message, count);\r
9465       else\r
9466       {\r
9467         dbgchk = wrap(msg, message, count, width, &line);\r
9468         if (dbgchk != len && appData.debugMode)\r
9469             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9470         ConsoleOutput(msg, len, FALSE);\r
9471         free(msg);\r
9472         return len;\r
9473       }\r
9474     }\r
9475   }\r
9476 \r
9477   if (ovl.hEvent == NULL) {\r
9478     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9479   }\r
9480   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9481 \r
9482   switch (cp->kind) {\r
9483   case CPSock:\r
9484   case CPRcmd:\r
9485     outCount = send(cp->sock, message, count, 0);\r
9486     if (outCount == SOCKET_ERROR) {\r
9487       *outError = WSAGetLastError();\r
9488     } else {\r
9489       *outError = NO_ERROR;\r
9490     }\r
9491     break;\r
9492 \r
9493   case CPReal:\r
9494     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9495                   &dOutCount, NULL)) {\r
9496       *outError = NO_ERROR;\r
9497       outCount = (int) dOutCount;\r
9498     } else {\r
9499       *outError = GetLastError();\r
9500     }\r
9501     break;\r
9502 \r
9503   case CPComm:\r
9504     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9505                             &dOutCount, &ovl);\r
9506     if (*outError == NO_ERROR) {\r
9507       outCount = (int) dOutCount;\r
9508     }\r
9509     break;\r
9510   }\r
9511   return outCount;\r
9512 }\r
9513 \r
9514 int\r
9515 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9516                        long msdelay)\r
9517 {\r
9518   /* Ignore delay, not implemented for WinBoard */\r
9519   return OutputToProcess(pr, message, count, outError);\r
9520 }\r
9521 \r
9522 \r
9523 void\r
9524 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9525                         char *buf, int count, int error)\r
9526 {\r
9527   DisplayFatalError(_("Not implemented"), 0, 1);\r
9528 }\r
9529 \r
9530 /* see wgamelist.c for Game List functions */\r
9531 /* see wedittags.c for Edit Tags functions */\r
9532 \r
9533 \r
9534 VOID\r
9535 ICSInitScript()\r
9536 {\r
9537   FILE *f;\r
9538   char buf[MSG_SIZ];\r
9539   char *dummy;\r
9540 \r
9541   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9542     f = fopen(buf, "r");\r
9543     if (f != NULL) {\r
9544       ProcessICSInitScript(f);\r
9545       fclose(f);\r
9546     }\r
9547   }\r
9548 }\r
9549 \r
9550 \r
9551 VOID\r
9552 StartAnalysisClock()\r
9553 {\r
9554   if (analysisTimerEvent) return;\r
9555   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9556                                         (UINT) 2000, NULL);\r
9557 }\r
9558 \r
9559 VOID\r
9560 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9561 {\r
9562   highlightInfo.sq[0].x = fromX;\r
9563   highlightInfo.sq[0].y = fromY;\r
9564   highlightInfo.sq[1].x = toX;\r
9565   highlightInfo.sq[1].y = toY;\r
9566 }\r
9567 \r
9568 VOID\r
9569 ClearHighlights()\r
9570 {\r
9571   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9572     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9573 }\r
9574 \r
9575 VOID\r
9576 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9577 {\r
9578   premoveHighlightInfo.sq[0].x = fromX;\r
9579   premoveHighlightInfo.sq[0].y = fromY;\r
9580   premoveHighlightInfo.sq[1].x = toX;\r
9581   premoveHighlightInfo.sq[1].y = toY;\r
9582 }\r
9583 \r
9584 VOID\r
9585 ClearPremoveHighlights()\r
9586 {\r
9587   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9588     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9589 }\r
9590 \r
9591 VOID\r
9592 ShutDownFrontEnd()\r
9593 {\r
9594   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9595   DeleteClipboardTempFiles();\r
9596 }\r
9597 \r
9598 void\r
9599 BoardToTop()\r
9600 {\r
9601     if (IsIconic(hwndMain))\r
9602       ShowWindow(hwndMain, SW_RESTORE);\r
9603 \r
9604     SetActiveWindow(hwndMain);\r
9605 }\r
9606 \r
9607 /*\r
9608  * Prototypes for animation support routines\r
9609  */\r
9610 static void ScreenSquare(int column, int row, POINT * pt);\r
9611 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9612      POINT frames[], int * nFrames);\r
9613 \r
9614 \r
9615 void\r
9616 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
9617 {       // [HGM] atomic: animate blast wave\r
9618         int i;\r
9619 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
9620         explodeInfo.fromX = fromX;\r
9621         explodeInfo.fromY = fromY;\r
9622         explodeInfo.toX = toX;\r
9623         explodeInfo.toY = toY;\r
9624         for(i=1; i<nFrames; i++) {\r
9625             explodeInfo.radius = (i*180)/(nFrames-1);\r
9626             DrawPosition(FALSE, NULL);\r
9627             Sleep(appData.animSpeed);\r
9628         }\r
9629         explodeInfo.radius = 0;\r
9630         DrawPosition(TRUE, NULL);\r
9631 }\r
9632 \r
9633 #define kFactor 4\r
9634 \r
9635 void\r
9636 AnimateMove(board, fromX, fromY, toX, toY)\r
9637      Board board;\r
9638      int fromX;\r
9639      int fromY;\r
9640      int toX;\r
9641      int toY;\r
9642 {\r
9643   ChessSquare piece;\r
9644   POINT start, finish, mid;\r
9645   POINT frames[kFactor * 2 + 1];\r
9646   int nFrames, n;\r
9647 \r
9648   if (!appData.animate) return;\r
9649   if (doingSizing) return;\r
9650   if (fromY < 0 || fromX < 0) return;\r
9651   piece = board[fromY][fromX];\r
9652   if (piece >= EmptySquare) return;\r
9653 \r
9654   ScreenSquare(fromX, fromY, &start);\r
9655   ScreenSquare(toX, toY, &finish);\r
9656 \r
9657   /* All pieces except knights move in straight line */\r
9658   if (piece != WhiteKnight && piece != BlackKnight) {\r
9659     mid.x = start.x + (finish.x - start.x) / 2;\r
9660     mid.y = start.y + (finish.y - start.y) / 2;\r
9661   } else {\r
9662     /* Knight: make diagonal movement then straight */\r
9663     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9664        mid.x = start.x + (finish.x - start.x) / 2;\r
9665        mid.y = finish.y;\r
9666      } else {\r
9667        mid.x = finish.x;\r
9668        mid.y = start.y + (finish.y - start.y) / 2;\r
9669      }\r
9670   }\r
9671   \r
9672   /* Don't use as many frames for very short moves */\r
9673   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9674     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9675   else\r
9676     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9677 \r
9678   animInfo.from.x = fromX;\r
9679   animInfo.from.y = fromY;\r
9680   animInfo.to.x = toX;\r
9681   animInfo.to.y = toY;\r
9682   animInfo.lastpos = start;\r
9683   animInfo.piece = piece;\r
9684   for (n = 0; n < nFrames; n++) {\r
9685     animInfo.pos = frames[n];\r
9686     DrawPosition(FALSE, NULL);\r
9687     animInfo.lastpos = animInfo.pos;\r
9688     Sleep(appData.animSpeed);\r
9689   }\r
9690   animInfo.pos = finish;\r
9691   DrawPosition(FALSE, NULL);\r
9692   animInfo.piece = EmptySquare;\r
9693   if(gameInfo.variant == VariantAtomic && \r
9694      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
9695         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
9696 }\r
9697 \r
9698 /*      Convert board position to corner of screen rect and color       */\r
9699 \r
9700 static void\r
9701 ScreenSquare(column, row, pt)\r
9702      int column; int row; POINT * pt;\r
9703 {\r
9704   if (flipView) {\r
9705     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9706     pt->y = lineGap + row * (squareSize + lineGap);\r
9707   } else {\r
9708     pt->x = lineGap + column * (squareSize + lineGap);\r
9709     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9710   }\r
9711 }\r
9712 \r
9713 /*      Generate a series of frame coords from start->mid->finish.\r
9714         The movement rate doubles until the half way point is\r
9715         reached, then halves back down to the final destination,\r
9716         which gives a nice slow in/out effect. The algorithmn\r
9717         may seem to generate too many intermediates for short\r
9718         moves, but remember that the purpose is to attract the\r
9719         viewers attention to the piece about to be moved and\r
9720         then to where it ends up. Too few frames would be less\r
9721         noticeable.                                             */\r
9722 \r
9723 static void\r
9724 Tween(start, mid, finish, factor, frames, nFrames)\r
9725      POINT * start; POINT * mid;\r
9726      POINT * finish; int factor;\r
9727      POINT frames[]; int * nFrames;\r
9728 {\r
9729   int n, fraction = 1, count = 0;\r
9730 \r
9731   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9732   for (n = 0; n < factor; n++)\r
9733     fraction *= 2;\r
9734   for (n = 0; n < factor; n++) {\r
9735     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9736     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9737     count ++;\r
9738     fraction = fraction / 2;\r
9739   }\r
9740   \r
9741   /* Midpoint */\r
9742   frames[count] = *mid;\r
9743   count ++;\r
9744   \r
9745   /* Slow out, stepping 1/2, then 1/4, ... */\r
9746   fraction = 2;\r
9747   for (n = 0; n < factor; n++) {\r
9748     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9749     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9750     count ++;\r
9751     fraction = fraction * 2;\r
9752   }\r
9753   *nFrames = count;\r
9754 }\r
9755 \r
9756 void\r
9757 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9758 {\r
9759     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9760 \r
9761     EvalGraphSet( first, last, current, pvInfoList );\r
9762 }\r