Merge branch 'v4.8.x'
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts.\r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free\r
9  * Software Foundation, Inc.\r
10  *\r
11  * Enhancements Copyright 2005 Alessandro Scotti\r
12  *\r
13  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
14  * which was written and is copyrighted by Wayne Christopher.\r
15  *\r
16  * The following terms apply to Digital Equipment Corporation's copyright\r
17  * interest in XBoard:\r
18  * ------------------------------------------------------------------------\r
19  * All Rights Reserved\r
20  *\r
21  * Permission to use, copy, modify, and distribute this software and its\r
22  * documentation for any purpose and without fee is hereby granted,\r
23  * provided that the above copyright notice appear in all copies and that\r
24  * both that copyright notice and this permission notice appear in\r
25  * supporting documentation, and that the name of Digital not be\r
26  * used in advertising or publicity pertaining to distribution of the\r
27  * software without specific, written prior permission.\r
28  *\r
29  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
30  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
31  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
32  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
33  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
34  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
35  * SOFTWARE.\r
36  * ------------------------------------------------------------------------\r
37  *\r
38  * The following terms apply to the enhanced version of XBoard\r
39  * distributed by the Free Software Foundation:\r
40  * ------------------------------------------------------------------------\r
41  *\r
42  * GNU XBoard is free software: you can redistribute it and/or modify\r
43  * it under the terms of the GNU General Public License as published by\r
44  * the Free Software Foundation, either version 3 of the License, or (at\r
45  * your option) any later version.\r
46  *\r
47  * GNU XBoard is distributed in the hope that it will be useful, but\r
48  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
49  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
50  * General Public License for more details.\r
51  *\r
52  * You should have received a copy of the GNU General Public License\r
53  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
54  *\r
55  *------------------------------------------------------------------------\r
56  ** See the file ChangeLog for a revision history.  */\r
57 \r
58 #include "config.h"\r
59 \r
60 #include <windows.h>\r
61 #include <winuser.h>\r
62 #include <winsock.h>\r
63 #include <commctrl.h>\r
64 \r
65 #include <stdio.h>\r
66 #include <stdlib.h>\r
67 #include <time.h>\r
68 #include <malloc.h>\r
69 #include <sys/stat.h>\r
70 #include <fcntl.h>\r
71 #include <math.h>\r
72 #include <commdlg.h>\r
73 #include <dlgs.h>\r
74 #include <richedit.h>\r
75 #include <mmsystem.h>\r
76 #include <ctype.h>\r
77 #include <io.h>\r
78 \r
79 #if __GNUC__\r
80 #include <errno.h>\r
81 #include <string.h>\r
82 #endif\r
83 \r
84 #include "common.h"\r
85 #include "frontend.h"\r
86 #include "backend.h"\r
87 #include "winboard.h"\r
88 #include "moves.h"\r
89 #include "wclipbrd.h"\r
90 #include "woptions.h"\r
91 #include "wsockerr.h"\r
92 #include "defaults.h"\r
93 #include "help.h"\r
94 #include "wsnap.h"\r
95 \r
96 #define SLASH '/'\r
97 #define DATADIR "~~"\r
98 \r
99 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
100 \r
101   int myrandom(void);\r
102   void mysrandom(unsigned int seed);\r
103 \r
104 extern int whiteFlag, blackFlag;\r
105 Boolean flipClock = FALSE;\r
106 extern HANDLE chatHandle[];\r
107 extern enum ICS_TYPE ics_type;\r
108 \r
109 int  MySearchPath P((char *installDir, char *name, char *fullname));\r
110 int  MyGetFullPathName P((char *name, char *fullname));\r
111 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
112 VOID NewVariantPopup(HWND hwnd);\r
113 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
114                    /*char*/int promoChar));\r
115 void DisplayMove P((int moveNumber));\r
116 void ChatPopUp P((char *s));\r
117 typedef struct {\r
118   ChessSquare piece;  \r
119   POINT pos;      /* window coordinates of current pos */\r
120   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
121   POINT from;     /* board coordinates of the piece's orig pos */\r
122   POINT to;       /* board coordinates of the piece's new pos */\r
123 } AnimInfo;\r
124 \r
125 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
126 \r
127 typedef struct {\r
128   POINT start;    /* window coordinates of start pos */\r
129   POINT pos;      /* window coordinates of current pos */\r
130   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
131   POINT from;     /* board coordinates of the piece's orig pos */\r
132   ChessSquare piece;\r
133 } DragInfo;\r
134 \r
135 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
136 \r
137 typedef struct {\r
138   POINT sq[2];    /* board coordinates of from, to squares */\r
139 } HighlightInfo;\r
140 \r
141 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
142 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
143 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
144 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
145 \r
146 typedef struct { // [HGM] atomic\r
147   int fromX, fromY, toX, toY, radius;\r
148 } ExplodeInfo;\r
149 \r
150 static ExplodeInfo explodeInfo;\r
151 \r
152 /* Window class names */\r
153 char szAppName[] = "WinBoard";\r
154 char szConsoleName[] = "WBConsole";\r
155 \r
156 /* Title bar text */\r
157 char szTitle[] = "WinBoard";\r
158 char szConsoleTitle[] = "I C S Interaction";\r
159 \r
160 char *programName;\r
161 char *settingsFileName;\r
162 Boolean saveSettingsOnExit;\r
163 char installDir[MSG_SIZ];\r
164 int errorExitStatus;\r
165 \r
166 BoardSize boardSize;\r
167 Boolean chessProgram;\r
168 //static int boardX, boardY;\r
169 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
170 int squareSize, lineGap, minorSize;\r
171 static int winW, winH;\r
172 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
173 static int logoHeight = 0;\r
174 static char messageText[MESSAGE_TEXT_MAX];\r
175 static int clockTimerEvent = 0;\r
176 static int loadGameTimerEvent = 0;\r
177 static int analysisTimerEvent = 0;\r
178 static DelayedEventCallback delayedTimerCallback;\r
179 static int delayedTimerEvent = 0;\r
180 static int buttonCount = 2;\r
181 char *icsTextMenuString;\r
182 char *icsNames;\r
183 char *firstChessProgramNames;\r
184 char *secondChessProgramNames;\r
185 \r
186 #define PALETTESIZE 256\r
187 \r
188 HINSTANCE hInst;          /* current instance */\r
189 Boolean alwaysOnTop = FALSE;\r
190 RECT boardRect;\r
191 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
192   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
193 COLORREF markerColor[8] = { 0x00FFFF, 0x0000FF, 0x00FF00, 0xFF0000, 0xFFFF00, 0xFF00FF, 0xFFFFFF, 0x000000 };\r
194 HPALETTE hPal;\r
195 ColorClass currentColorClass;\r
196 \r
197 static HWND savedHwnd;\r
198 HWND hCommPort = NULL;    /* currently open comm port */\r
199 static HWND hwndPause;    /* pause button */\r
200 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
201 static HBRUSH lightSquareBrush, darkSquareBrush,\r
202   blackSquareBrush, /* [HGM] for band between board and holdings */\r
203   explodeBrush,     /* [HGM] atomic */\r
204   markerBrush[8],   /* [HGM] markers */\r
205   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
206 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
207 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
208 static HPEN gridPen = NULL;\r
209 static HPEN highlightPen = NULL;\r
210 static HPEN premovePen = NULL;\r
211 static NPLOGPALETTE pLogPal;\r
212 static BOOL paletteChanged = FALSE;\r
213 static HICON iconWhite, iconBlack, iconCurrent;\r
214 static int doingSizing = FALSE;\r
215 static int lastSizing = 0;\r
216 static int prevStderrPort;\r
217 static HBITMAP userLogo;\r
218 \r
219 static HBITMAP liteBackTexture = NULL;\r
220 static HBITMAP darkBackTexture = NULL;\r
221 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
222 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
223 static int backTextureSquareSize = 0;\r
224 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
225 \r
226 #if __GNUC__ && !defined(_winmajor)\r
227 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
228 #else\r
229 \r
230 #if defined(_winmajor)\r
231 #define oldDialog (_winmajor < 4)\r
232 #else\r
233 #define oldDialog 0\r
234 #endif\r
235 #endif\r
236 \r
237 #define INTERNATIONAL\r
238 \r
239 #ifdef INTERNATIONAL\r
240 #  define _(s) T_(s)\r
241 #  define N_(s) s\r
242 #else\r
243 #  define _(s) s\r
244 #  define N_(s) s\r
245 #  define T_(s) s\r
246 #  define Translate(x, y)\r
247 #  define LoadLanguageFile(s)\r
248 #endif\r
249 \r
250 #ifdef INTERNATIONAL\r
251 \r
252 Boolean barbaric; // flag indicating if translation is needed\r
253 \r
254 // list of item numbers used in each dialog (used to alter language at run time)\r
255 \r
256 #define ABOUTBOX -1  /* not sure why these are needed */\r
257 #define ABOUTBOX2 -1\r
258 \r
259 int dialogItems[][42] = {\r
260 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
261 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
262   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
263 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,\r
264   OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds,\r
265   OPT_Ranget, IDOK, IDCANCEL }, \r
266 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
267   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
268 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
269 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
270   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
271 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
272 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
273   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
274 { ABOUTBOX2, IDC_ChessBoard }, \r
275 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
276   OPT_GameListClose, IDC_GameListDoFilter }, \r
277 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
278 { DLG_Error, IDOK }, \r
279 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
280   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
281 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
282 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
283   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
284   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
285 { DLG_IndexNumber, IDC_Index }, \r
286 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
287 { DLG_TypeInName, IDOK, IDCANCEL }, \r
288 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
289   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
290 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
291   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
292   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
293   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
294   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
295   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
296   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
297 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
298   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
299   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
300   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
301   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
302   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
303   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
304   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
305   GPB_General, GPB_Alarm, OPT_AutoCreate }, \r
306 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
307   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
308   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
309   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
310   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
311   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
312   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
313   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
314 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
315   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
316   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
317   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
318   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
319   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
320   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
321   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
322   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
323 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
324   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
325   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
326   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
327   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
328 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
329 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
330   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
331 { DLG_MoveHistory }, \r
332 { DLG_EvalGraph }, \r
333 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
334 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
335 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
336   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
337   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
338   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
339 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
340   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
341   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
342 { 0 }\r
343 };\r
344 \r
345 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
346 static int lastChecked;\r
347 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
348 extern int tinyLayout;\r
349 extern char * menuBarText[][10];\r
350 \r
351 void\r
352 LoadLanguageFile(char *name)\r
353 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
354     FILE *f;\r
355     int i=0, j=0, n=0, k;\r
356     char buf[MSG_SIZ];\r
357 \r
358     if(!name || name[0] == NULLCHAR) return;\r
359       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
360     appData.language = oldLanguage;\r
361     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
362     if((f = fopen(buf, "r")) == NULL) return;\r
363     while((k = fgetc(f)) != EOF) {\r
364         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
365         languageBuf[i] = k;\r
366         if(k == '\n') {\r
367             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
368                 char *p;\r
369                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
370                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
371                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
372                         english[j] = languageBuf + n + 1; *p = 0;\r
373                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
374 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
375                     }\r
376                 }\r
377             }\r
378             n = i + 1;\r
379         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
380             switch(k) {\r
381               case 'n': k = '\n'; break;\r
382               case 'r': k = '\r'; break;\r
383               case 't': k = '\t'; break;\r
384             }\r
385             languageBuf[--i] = k;\r
386         }\r
387         i++;\r
388     }\r
389     fclose(f);\r
390     barbaric = (j != 0);\r
391     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
392 }\r
393 \r
394 char *\r
395 T_(char *s)\r
396 {   // return the translation of the given string\r
397     // efficiency can be improved a lot...\r
398     int i=0;\r
399     static char buf[MSG_SIZ];\r
400 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
401     if(!barbaric) return s;\r
402     if(!s) return ""; // sanity\r
403     while(english[i]) {\r
404         if(!strcmp(s, english[i])) return foreign[i];\r
405         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
406             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
407             return buf;\r
408         }\r
409         i++;\r
410     }\r
411     return s;\r
412 }\r
413 \r
414 void\r
415 Translate(HWND hDlg, int dialogID)\r
416 {   // translate all text items in the given dialog\r
417     int i=0, j, k;\r
418     char buf[MSG_SIZ], *s;\r
419     if(!barbaric) return;\r
420     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
421     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
422     GetWindowText( hDlg, buf, MSG_SIZ );\r
423     s = T_(buf);\r
424     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
425     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
426         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
427         if(strlen(buf) == 0) continue;\r
428         s = T_(buf);\r
429         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
430     }\r
431 }\r
432 \r
433 HMENU\r
434 TranslateOneMenu(int i, HMENU subMenu)\r
435 {\r
436     int j;\r
437     static MENUITEMINFO info;\r
438 \r
439     info.cbSize = sizeof(MENUITEMINFO);\r
440     info.fMask = MIIM_STATE | MIIM_TYPE;\r
441           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
442             char buf[MSG_SIZ];\r
443             info.dwTypeData = buf;\r
444             info.cch = sizeof(buf);\r
445             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
446             if(i < 10) {\r
447                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
448                 else menuText[i][j] = strdup(buf); // remember original on first change\r
449             }\r
450             if(buf[0] == NULLCHAR) continue;\r
451             info.dwTypeData = T_(buf);\r
452             info.cch = strlen(buf)+1;\r
453             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
454           }\r
455     return subMenu;\r
456 }\r
457 \r
458 void\r
459 TranslateMenus(int addLanguage)\r
460 {\r
461     int i;\r
462     WIN32_FIND_DATA fileData;\r
463     HANDLE hFind;\r
464 #define IDM_English 1970\r
465     if(1) {\r
466         HMENU mainMenu = GetMenu(hwndMain);\r
467         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
468           HMENU subMenu = GetSubMenu(mainMenu, i);\r
469           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
470                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
471           TranslateOneMenu(i, subMenu);\r
472         }\r
473         DrawMenuBar(hwndMain);\r
474     }\r
475 \r
476     if(!addLanguage) return;\r
477     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
478         HMENU mainMenu = GetMenu(hwndMain);\r
479         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
480         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
481         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
482         i = 0; lastChecked = IDM_English;\r
483         do {\r
484             char *p, *q = fileData.cFileName;\r
485             int checkFlag = MF_UNCHECKED;\r
486             languageFile[i] = strdup(q);\r
487             if(barbaric && !strcmp(oldLanguage, q)) {\r
488                 checkFlag = MF_CHECKED;\r
489                 lastChecked = IDM_English + i + 1;\r
490                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
491             }\r
492             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
493             p = strstr(fileData.cFileName, ".lng");\r
494             if(p) *p = 0;\r
495             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
496         } while(FindNextFile(hFind, &fileData));\r
497         FindClose(hFind);\r
498     }\r
499 }\r
500 \r
501 #endif\r
502 \r
503 #define IDM_RecentEngines 3000\r
504 \r
505 void\r
506 RecentEngineMenu (char *s)\r
507 {\r
508     if(appData.icsActive) return;\r
509     if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
510         HMENU mainMenu = GetMenu(hwndMain);\r
511         HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
512         int i=IDM_RecentEngines;\r
513         recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
514         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
515         while(*s) {\r
516           char *p = strchr(s, '\n');\r
517           if(p == NULL) return; // malformed!\r
518           *p = NULLCHAR;\r
519           AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
520           *p = '\n';\r
521           s = p+1;\r
522         }\r
523     }\r
524 }\r
525 \r
526 \r
527 typedef struct {\r
528   char *name;\r
529   int squareSize;\r
530   int lineGap;\r
531   int smallLayout;\r
532   int tinyLayout;\r
533   int cliWidth, cliHeight;\r
534 } SizeInfo;\r
535 \r
536 SizeInfo sizeInfo[] = \r
537 {\r
538   { "tiny",     21, 0, 1, 2, 0, 0 },\r
539   { "teeny",    25, 1, 1, 2, 0, 0 },\r
540   { "dinky",    29, 1, 1, 2, 0, 0 },\r
541   { "petite",   33, 1, 1, 2, 0, 0 },\r
542   { "slim",     37, 2, 1, 1, 0, 0 },\r
543   { "small",    40, 2, 1, 1, 0, 0 },\r
544   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
545   { "middling", 49, 2, 0, 0, 0, 0 },\r
546   { "average",  54, 2, 0, 0, 0, 0 },\r
547   { "moderate", 58, 3, 0, 0, 0, 0 },\r
548   { "medium",   64, 3, 0, 0, 0, 0 },\r
549   { "bulky",    72, 3, 0, 0, 0, 0 },\r
550   { "large",    80, 3, 0, 0, 0, 0 },\r
551   { "big",      87, 3, 0, 0, 0, 0 },\r
552   { "huge",     95, 3, 0, 0, 0, 0 },\r
553   { "giant",    108, 3, 0, 0, 0, 0 },\r
554   { "colossal", 116, 4, 0, 0, 0, 0 },\r
555   { "titanic",  129, 4, 0, 0, 0, 0 },\r
556   { NULL, 0, 0, 0, 0, 0, 0 }\r
557 };\r
558 \r
559 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
560 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
561 {\r
562   { 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), MF(GAMELIST_FONT_ALL) },\r
563   { 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), MF(GAMELIST_FONT_ALL) },\r
564   { 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), MF(GAMELIST_FONT_ALL) },\r
565   { 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), MF(GAMELIST_FONT_ALL) },\r
566   { 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), MF(GAMELIST_FONT_ALL) },\r
567   { 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), MF(GAMELIST_FONT_ALL) },\r
568   { 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), MF(GAMELIST_FONT_ALL) },\r
569   { 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), MF(GAMELIST_FONT_ALL) },\r
570   { 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), MF(GAMELIST_FONT_ALL) },\r
571   { 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), MF(GAMELIST_FONT_ALL) },\r
572   { 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),  MF(GAMELIST_FONT_ALL) },\r
573   { 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),  MF(GAMELIST_FONT_ALL) },\r
574   { 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),  MF(GAMELIST_FONT_ALL) },\r
575   { 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),  MF(GAMELIST_FONT_ALL) },\r
576   { 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), MF(GAMELIST_FONT_ALL) },\r
577   { 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), MF(GAMELIST_FONT_ALL) },\r
578   { 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), MF (GAMELIST_FONT_ALL) },\r
579   { 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), MF(GAMELIST_FONT_ALL) },\r
580 };\r
581 \r
582 MyFont *font[NUM_SIZES][NUM_FONTS];\r
583 \r
584 typedef struct {\r
585   char *label;\r
586   int id;\r
587   HWND hwnd;\r
588   WNDPROC wndproc;\r
589 } MyButtonDesc;\r
590 \r
591 #define BUTTON_WIDTH (tinyLayout == 2 ? 16 : 32)\r
592 #define N_BUTTONS 5\r
593 \r
594 MyButtonDesc buttonDesc[N_BUTTONS] =\r
595 {\r
596   {"<<", IDM_ToStart, NULL, NULL},\r
597   {"<", IDM_Backward, NULL, NULL},\r
598   {"P", IDM_Pause, NULL, NULL},\r
599   {">", IDM_Forward, NULL, NULL},\r
600   {">>", IDM_ToEnd, NULL, NULL},\r
601 };\r
602 \r
603 int tinyLayout = 0, smallLayout = 0;\r
604 #define MENU_BAR_ITEMS 9\r
605 char *menuBarText[3][MENU_BAR_ITEMS+1] = {\r
606   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
607   { N_("&Fil"), N_("&Ed"), N_("&Vw"), N_("&Mod"), N_("&Act"), N_("E&ng"), N_("&Opt"), N_("&Hlp"), NULL },\r
608   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
609 };\r
610 \r
611 \r
612 MySound sounds[(int)NSoundClasses];\r
613 MyTextAttribs textAttribs[(int)NColorClasses];\r
614 \r
615 MyColorizeAttribs colorizeAttribs[] = {\r
616   { (COLORREF)0, 0, N_("Shout Text") },\r
617   { (COLORREF)0, 0, N_("SShout/CShout") },\r
618   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
619   { (COLORREF)0, 0, N_("Channel Text") },\r
620   { (COLORREF)0, 0, N_("Kibitz Text") },\r
621   { (COLORREF)0, 0, N_("Tell Text") },\r
622   { (COLORREF)0, 0, N_("Challenge Text") },\r
623   { (COLORREF)0, 0, N_("Request Text") },\r
624   { (COLORREF)0, 0, N_("Seek Text") },\r
625   { (COLORREF)0, 0, N_("Normal Text") },\r
626   { (COLORREF)0, 0, N_("None") }\r
627 };\r
628 \r
629 \r
630 \r
631 static char *commentTitle;\r
632 static char *commentText;\r
633 static int commentIndex;\r
634 static Boolean editComment = FALSE;\r
635 \r
636 \r
637 char errorTitle[MSG_SIZ];\r
638 char errorMessage[2*MSG_SIZ];\r
639 HWND errorDialog = NULL;\r
640 BOOLEAN moveErrorMessageUp = FALSE;\r
641 BOOLEAN consoleEcho = TRUE;\r
642 CHARFORMAT consoleCF;\r
643 COLORREF consoleBackgroundColor;\r
644 \r
645 char *programVersion;\r
646 \r
647 #define CPReal 1\r
648 #define CPComm 2\r
649 #define CPSock 3\r
650 #define CPRcmd 4\r
651 typedef int CPKind;\r
652 \r
653 typedef struct {\r
654   CPKind kind;\r
655   HANDLE hProcess;\r
656   DWORD pid;\r
657   HANDLE hTo;\r
658   HANDLE hFrom;\r
659   SOCKET sock;\r
660   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
661 } ChildProc;\r
662 \r
663 #define INPUT_SOURCE_BUF_SIZE 4096\r
664 \r
665 typedef struct _InputSource {\r
666   CPKind kind;\r
667   HANDLE hFile;\r
668   SOCKET sock;\r
669   int lineByLine;\r
670   HANDLE hThread;\r
671   DWORD id;\r
672   char buf[INPUT_SOURCE_BUF_SIZE];\r
673   char *next;\r
674   DWORD count;\r
675   int error;\r
676   InputCallback func;\r
677   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
678   VOIDSTAR closure;\r
679 } InputSource;\r
680 \r
681 InputSource *consoleInputSource;\r
682 \r
683 DCB dcb;\r
684 \r
685 /* forward */\r
686 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
687 VOID ConsoleCreate();\r
688 LRESULT CALLBACK\r
689   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
690 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
691 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
692 VOID ParseCommSettings(char *arg, DCB *dcb);\r
693 LRESULT CALLBACK\r
694   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
695 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
696 void ParseIcsTextMenu(char *icsTextMenuString);\r
697 VOID PopUpNameDialog(char firstchar);\r
698 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
699 \r
700 /* [AS] */\r
701 int NewGameFRC();\r
702 int GameListOptions();\r
703 \r
704 int dummy; // [HGM] for obsolete args\r
705 \r
706 HWND hwndMain = NULL;        /* root window*/\r
707 HWND hwndConsole = NULL;\r
708 HWND commentDialog = NULL;\r
709 HWND moveHistoryDialog = NULL;\r
710 HWND evalGraphDialog = NULL;\r
711 HWND engineOutputDialog = NULL;\r
712 HWND gameListDialog = NULL;\r
713 HWND editTagsDialog = NULL;\r
714 \r
715 int commentUp = FALSE;\r
716 \r
717 WindowPlacement wpMain;\r
718 WindowPlacement wpConsole;\r
719 WindowPlacement wpComment;\r
720 WindowPlacement wpMoveHistory;\r
721 WindowPlacement wpEvalGraph;\r
722 WindowPlacement wpEngineOutput;\r
723 WindowPlacement wpGameList;\r
724 WindowPlacement wpTags;\r
725 \r
726 VOID EngineOptionsPopup(); // [HGM] settings\r
727 \r
728 VOID GothicPopUp(char *title, VariantClass variant);\r
729 /*\r
730  * Setting "frozen" should disable all user input other than deleting\r
731  * the window.  We do this while engines are initializing themselves.\r
732  */\r
733 static int frozen = 0;\r
734 static int oldMenuItemState[MENU_BAR_ITEMS];\r
735 void FreezeUI()\r
736 {\r
737   HMENU hmenu;\r
738   int i;\r
739 \r
740   if (frozen) return;\r
741   frozen = 1;\r
742   hmenu = GetMenu(hwndMain);\r
743   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
744     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
745   }\r
746   DrawMenuBar(hwndMain);\r
747 }\r
748 \r
749 /* Undo a FreezeUI */\r
750 void ThawUI()\r
751 {\r
752   HMENU hmenu;\r
753   int i;\r
754 \r
755   if (!frozen) return;\r
756   frozen = 0;\r
757   hmenu = GetMenu(hwndMain);\r
758   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
759     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
760   }\r
761   DrawMenuBar(hwndMain);\r
762 }\r
763 \r
764 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
765 \r
766 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
767 #ifdef JAWS\r
768 #include "jaws.c"\r
769 #else\r
770 #define JAWS_INIT\r
771 #define JAWS_ARGS\r
772 #define JAWS_ALT_INTERCEPT\r
773 #define JAWS_KBUP_NAVIGATION\r
774 #define JAWS_KBDOWN_NAVIGATION\r
775 #define JAWS_MENU_ITEMS\r
776 #define JAWS_SILENCE\r
777 #define JAWS_REPLAY\r
778 #define JAWS_ACCEL\r
779 #define JAWS_COPYRIGHT\r
780 #define JAWS_DELETE(X) X\r
781 #define SAYMACHINEMOVE()\r
782 #define SAY(X)\r
783 #endif\r
784 \r
785 /*---------------------------------------------------------------------------*\\r
786  *\r
787  * WinMain\r
788  *\r
789 \*---------------------------------------------------------------------------*/\r
790 \r
791 static void HandleMessage P((MSG *message));\r
792 static HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
793 \r
794 int APIENTRY\r
795 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
796         LPSTR lpCmdLine, int nCmdShow)\r
797 {\r
798   MSG msg;\r
799 //  INITCOMMONCONTROLSEX ex;\r
800 \r
801   debugFP = stderr;\r
802 \r
803   LoadLibrary("RICHED32.DLL");\r
804   consoleCF.cbSize = sizeof(CHARFORMAT);\r
805 \r
806   if (!InitApplication(hInstance)) {\r
807     return (FALSE);\r
808   }\r
809   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
810     return (FALSE);\r
811   }\r
812 \r
813   JAWS_INIT\r
814   TranslateMenus(1);\r
815 \r
816 //  InitCommonControlsEx(&ex);\r
817   InitCommonControls();\r
818 \r
819   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
820   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
821   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
822 \r
823   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
824 \r
825   while (GetMessage(&msg, /* message structure */\r
826                     NULL, /* handle of window receiving the message */\r
827                     0,    /* lowest message to examine */\r
828                     0))   /* highest message to examine */\r
829     {\r
830         HandleMessage(&msg);\r
831     }\r
832 \r
833 \r
834   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
835 }\r
836 \r
837 static void\r
838 HandleMessage (MSG *message)\r
839 {\r
840     MSG msg = *message;\r
841 \r
842       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
843         // [HGM] navigate: switch between all windows with tab\r
844         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
845         int i, currentElement = 0;\r
846 \r
847         // first determine what element of the chain we come from (if any)\r
848         if(appData.icsActive) {\r
849             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
850             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
851         }\r
852         if(engineOutputDialog && EngineOutputIsUp()) {\r
853             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
854             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
855         }\r
856         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
857             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
858         }\r
859         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
860         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
861         if(msg.hwnd == e1)                 currentElement = 2; else\r
862         if(msg.hwnd == e2)                 currentElement = 3; else\r
863         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
864         if(msg.hwnd == mh)                currentElement = 4; else\r
865         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
866         if(msg.hwnd == hText)  currentElement = 5; else\r
867         if(msg.hwnd == hInput) currentElement = 6; else\r
868         for (i = 0; i < N_BUTTONS; i++) {\r
869             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
870         }\r
871 \r
872         // determine where to go to\r
873         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
874           do {\r
875             currentElement = (currentElement + direction) % 7;\r
876             switch(currentElement) {\r
877                 case 0:\r
878                   h = hwndMain; break; // passing this case always makes the loop exit\r
879                 case 1:\r
880                   h = buttonDesc[0].hwnd; break; // could be NULL\r
881                 case 2:\r
882                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
883                   h = e1; break;\r
884                 case 3:\r
885                   if(!EngineOutputIsUp()) continue;\r
886                   h = e2; break;\r
887                 case 4:\r
888                   if(!MoveHistoryIsUp()) continue;\r
889                   h = mh; break;\r
890 //              case 6: // input to eval graph does not seem to get here!\r
891 //                if(!EvalGraphIsUp()) continue;\r
892 //                h = evalGraphDialog; break;\r
893                 case 5:\r
894                   if(!appData.icsActive) continue;\r
895                   SAY("display");\r
896                   h = hText; break;\r
897                 case 6:\r
898                   if(!appData.icsActive) continue;\r
899                   SAY("input");\r
900                   h = hInput; break;\r
901             }\r
902           } while(h == 0);\r
903 \r
904           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
905           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
906           SetFocus(h);\r
907 \r
908           return; // this message now has been processed\r
909         }\r
910       }\r
911 \r
912       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
913           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
914           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
915           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
916           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
917           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
918           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
919           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
920           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
921           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
922         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
923         for(i=0; i<MAX_CHAT; i++) \r
924             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
925                 done = 1; break;\r
926         }\r
927         if(done) return; // [HGM] chat: end patch\r
928         TranslateMessage(&msg); /* Translates virtual key codes */\r
929         DispatchMessage(&msg);  /* Dispatches message to window */\r
930       }\r
931 }\r
932 \r
933 void\r
934 DoEvents ()\r
935 { /* Dispatch pending messages */\r
936   MSG msg;\r
937   while (PeekMessage(&msg, /* message structure */\r
938                      NULL, /* handle of window receiving the message */\r
939                      0,    /* lowest message to examine */\r
940                      0,    /* highest message to examine */\r
941                      PM_REMOVE))\r
942     {\r
943         HandleMessage(&msg);\r
944     }\r
945 }\r
946 \r
947 /*---------------------------------------------------------------------------*\\r
948  *\r
949  * Initialization functions\r
950  *\r
951 \*---------------------------------------------------------------------------*/\r
952 \r
953 void\r
954 SetUserLogo()\r
955 {   // update user logo if necessary\r
956     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
957 \r
958     if(appData.autoLogo) {\r
959           curName = UserName();\r
960           if(strcmp(curName, oldUserName)) {\r
961                 GetCurrentDirectory(MSG_SIZ, dir);\r
962                 SetCurrentDirectory(installDir);\r
963                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
964                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
965                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
966                 if(userLogo == NULL)\r
967                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
968                 SetCurrentDirectory(dir); /* return to prev directory */\r
969           }\r
970     }\r
971 }\r
972 \r
973 BOOL\r
974 InitApplication(HINSTANCE hInstance)\r
975 {\r
976   WNDCLASS wc;\r
977 \r
978   /* Fill in window class structure with parameters that describe the */\r
979   /* main window. */\r
980 \r
981   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
982   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
983   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
984   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
985   wc.hInstance     = hInstance;         /* Owner of this class */\r
986   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
987   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
988   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
989   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
990   wc.lpszClassName = szAppName;                 /* Name to register as */\r
991 \r
992   /* Register the window class and return success/failure code. */\r
993   if (!RegisterClass(&wc)) return FALSE;\r
994 \r
995   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
996   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
997   wc.cbClsExtra    = 0;\r
998   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
999   wc.hInstance     = hInstance;\r
1000   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
1001   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
1002   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
1003   wc.lpszMenuName  = NULL;\r
1004   wc.lpszClassName = szConsoleName;\r
1005 \r
1006   if (!RegisterClass(&wc)) return FALSE;\r
1007   return TRUE;\r
1008 }\r
1009 \r
1010 \r
1011 /* Set by InitInstance, used by EnsureOnScreen */\r
1012 int screenHeight, screenWidth;\r
1013 RECT screenGeometry;\r
1014 \r
1015 void\r
1016 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
1017 {\r
1018 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
1019   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
1020   if (*x > screenGeometry.right - 32) *x = screenGeometry.left;\r
1021   if (*y > screenGeometry.bottom - 32) *y = screenGeometry.top;\r
1022   if (*x < screenGeometry.left + minX) *x = screenGeometry.left + minX;\r
1023   if (*y < screenGeometry.top + minY) *y = screenGeometry.top + minY;\r
1024 }\r
1025 \r
1026 VOID\r
1027 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
1028 {\r
1029   char buf[MSG_SIZ], dir[MSG_SIZ];\r
1030   GetCurrentDirectory(MSG_SIZ, dir);\r
1031   SetCurrentDirectory(installDir);\r
1032   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
1033       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1034 \r
1035       if (cps->programLogo == NULL && appData.debugMode) {\r
1036           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1037       }\r
1038   } else if(appData.autoLogo) {\r
1039       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1040         char *opponent = "";\r
1041         if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;\r
1042         if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;\r
1043         sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);\r
1044         if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {\r
1045             sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1046             cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1047         }\r
1048       } else\r
1049       if(appData.directory[n] && appData.directory[n][0]) {\r
1050         SetCurrentDirectory(appData.directory[n]);\r
1051         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1052       }\r
1053   }\r
1054   SetCurrentDirectory(dir); /* return to prev directory */\r
1055 }\r
1056 \r
1057 VOID\r
1058 InitTextures()\r
1059 {\r
1060   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1061   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1062   \r
1063   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1064       if(liteBackTexture) DeleteObject(liteBackTexture);\r
1065       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1066       liteBackTextureMode = appData.liteBackTextureMode;\r
1067 \r
1068       if (liteBackTexture == NULL && appData.debugMode) {\r
1069           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1070       }\r
1071   }\r
1072   \r
1073   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1074       if(darkBackTexture) DeleteObject(darkBackTexture);\r
1075       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1076       darkBackTextureMode = appData.darkBackTextureMode;\r
1077 \r
1078       if (darkBackTexture == NULL && appData.debugMode) {\r
1079           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1080       }\r
1081   }\r
1082 }\r
1083 \r
1084 #ifndef SM_CXVIRTUALSCREEN\r
1085 #define SM_CXVIRTUALSCREEN 78\r
1086 #endif\r
1087 #ifndef SM_CYVIRTUALSCREEN\r
1088 #define SM_CYVIRTUALSCREEN 79\r
1089 #endif\r
1090 #ifndef SM_XVIRTUALSCREEN \r
1091 #define SM_XVIRTUALSCREEN 76\r
1092 #endif\r
1093 #ifndef SM_YVIRTUALSCREEN \r
1094 #define SM_YVIRTUALSCREEN 77\r
1095 #endif\r
1096 \r
1097 VOID\r
1098 InitGeometry()\r
1099 {\r
1100   screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);\r
1101   if( !screenHeight ) screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1102   screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);\r
1103   if( !screenWidth ) screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1104   screenGeometry.left = GetSystemMetrics(SM_XVIRTUALSCREEN);\r
1105   screenGeometry.top = GetSystemMetrics(SM_YVIRTUALSCREEN);\r
1106   screenGeometry.right = screenGeometry.left + screenWidth;\r
1107   screenGeometry.bottom = screenGeometry.top + screenHeight;\r
1108 }\r
1109 \r
1110 ChessProgramState broadcast;\r
1111 \r
1112 BOOL\r
1113 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1114 {\r
1115   HWND hwnd; /* Main window handle. */\r
1116   int ibs;\r
1117   WINDOWPLACEMENT wp;\r
1118   char *filepart;\r
1119 \r
1120   hInst = hInstance;    /* Store instance handle in our global variable */\r
1121   programName = szAppName;\r
1122 \r
1123   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1124     *filepart = NULLCHAR;\r
1125     SetCurrentDirectory(installDir);\r
1126   } else {\r
1127     GetCurrentDirectory(MSG_SIZ, installDir);\r
1128   }\r
1129   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1130   InitGeometry();\r
1131   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1132   /* xboard, and older WinBoards, controlled the move sound with the\r
1133      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1134      always turn the option on (so that the backend will call us),\r
1135      then let the user turn the sound off by setting it to silence if\r
1136      desired.  To accommodate old winboard.ini files saved by old\r
1137      versions of WinBoard, we also turn off the sound if the option\r
1138      was initially set to false. [HGM] taken out of InitAppData */\r
1139   if (!appData.ringBellAfterMoves) {\r
1140     sounds[(int)SoundMove].name = strdup("");\r
1141     appData.ringBellAfterMoves = TRUE;\r
1142   }\r
1143   if (appData.debugMode) {\r
1144     char *c = appData.nameOfDebugFile;\r
1145     if(strstr(c, "///") == c) {\r
1146       broadcast.which = "broadcaster";\r
1147       broadcast.pr   = NoProc;\r
1148       broadcast.isr  = NULL;\r
1149       broadcast.program = c + 3;\r
1150       broadcast.dir  = ".";\r
1151       broadcast.host = "localhost";\r
1152       StartChessProgram(&broadcast);\r
1153       debugFP = (FILE*) _fdopen(_open_osfhandle((long)(((ChildProc*)(broadcast.pr))->hTo), _O_WRONLY), "w");\r
1154     } else\r
1155     debugFP = fopen(c, "w");\r
1156     setbuf(debugFP, NULL);\r
1157   }\r
1158 \r
1159   LoadLanguageFile(appData.language);\r
1160 \r
1161   InitBackEnd1();\r
1162 \r
1163 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1164 //  InitEngineUCI( installDir, &second );\r
1165 \r
1166   /* Create a main window for this application instance. */\r
1167   hwnd = CreateWindow(szAppName, szTitle,\r
1168                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1169                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1170                       NULL, NULL, hInstance, NULL);\r
1171   hwndMain = hwnd;\r
1172 \r
1173   /* If window could not be created, return "failure" */\r
1174   if (!hwnd) {\r
1175     return (FALSE);\r
1176   }\r
1177 \r
1178   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1179   LoadLogo(&first, 0, FALSE);\r
1180   LoadLogo(&second, 1, appData.icsActive);\r
1181 \r
1182   SetUserLogo();\r
1183 \r
1184   iconWhite = LoadIcon(hInstance, "icon_white");\r
1185   iconBlack = LoadIcon(hInstance, "icon_black");\r
1186   iconCurrent = iconWhite;\r
1187   InitDrawingColors();\r
1188 \r
1189   InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args\r
1190   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1191     /* Compute window size for each board size, and use the largest\r
1192        size that fits on this screen as the default. */\r
1193     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1194     if (boardSize == (BoardSize)-1 &&\r
1195         winH <= screenHeight\r
1196            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1197         && winW <= screenWidth) {\r
1198       boardSize = (BoardSize)ibs;\r
1199     }\r
1200   }\r
1201 \r
1202   InitDrawingSizes(boardSize, 0);\r
1203   RecentEngineMenu(appData.recentEngineList);\r
1204   InitMenuChecks();\r
1205   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1206 \r
1207   /* [AS] Load textures if specified */\r
1208   InitTextures();\r
1209 \r
1210   mysrandom( (unsigned) time(NULL) );\r
1211 \r
1212   /* [AS] Restore layout */\r
1213   if( wpMoveHistory.visible ) {\r
1214       MoveHistoryPopUp();\r
1215   }\r
1216 \r
1217   if( wpEvalGraph.visible ) {\r
1218       EvalGraphPopUp();\r
1219   }\r
1220 \r
1221   if( wpEngineOutput.visible ) {\r
1222       EngineOutputPopUp();\r
1223   }\r
1224 \r
1225   /* Make the window visible; update its client area; and return "success" */\r
1226   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1227   wp.length = sizeof(WINDOWPLACEMENT);\r
1228   wp.flags = 0;\r
1229   wp.showCmd = nCmdShow;\r
1230   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1231   wp.rcNormalPosition.left = wpMain.x;\r
1232   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1233   wp.rcNormalPosition.top = wpMain.y;\r
1234   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1235   SetWindowPlacement(hwndMain, &wp);\r
1236 \r
1237   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1238 \r
1239   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1240                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1241 \r
1242   if (hwndConsole) {\r
1243 #if AOT_CONSOLE\r
1244     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1245                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1246 #endif\r
1247     ShowWindow(hwndConsole, nCmdShow);\r
1248     SetActiveWindow(hwndConsole);\r
1249   }\r
1250   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1251   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1252 \r
1253   return TRUE;\r
1254 \r
1255 }\r
1256 \r
1257 VOID\r
1258 InitMenuChecks()\r
1259 {\r
1260   HMENU hmenu = GetMenu(hwndMain);\r
1261 \r
1262   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1263                         MF_BYCOMMAND|((appData.icsActive &&\r
1264                                        *appData.icsCommPort != NULLCHAR) ?\r
1265                                       MF_ENABLED : MF_GRAYED));\r
1266   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1267                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1268                                      MF_CHECKED : MF_UNCHECKED));\r
1269   EnableMenuItem(hmenu, IDM_SaveSelected, MF_GRAYED);\r
1270 }\r
1271 \r
1272 //---------------------------------------------------------------------------------------------------------\r
1273 \r
1274 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1275 #define XBOARD FALSE\r
1276 \r
1277 #define OPTCHAR "/"\r
1278 #define SEPCHAR "="\r
1279 #define TOPLEVEL 0\r
1280 \r
1281 #include "args.h"\r
1282 \r
1283 // front-end part of option handling\r
1284 \r
1285 VOID\r
1286 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1287 {\r
1288   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1289   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1290   DeleteDC(hdc);\r
1291   lf->lfWidth = 0;\r
1292   lf->lfEscapement = 0;\r
1293   lf->lfOrientation = 0;\r
1294   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1295   lf->lfItalic = mfp->italic;\r
1296   lf->lfUnderline = mfp->underline;\r
1297   lf->lfStrikeOut = mfp->strikeout;\r
1298   lf->lfCharSet = mfp->charset;\r
1299   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1300 \r
1301 \r
1302 \r
1303   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1304   lf->lfQuality = DEFAULT_QUALITY;\r
1305   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1306     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1307 }\r
1308 \r
1309 void\r
1310 CreateFontInMF(MyFont *mf)\r
1311\r
1312   LFfromMFP(&mf->lf, &mf->mfp);\r
1313   if (mf->hf) DeleteObject(mf->hf);\r
1314   mf->hf = CreateFontIndirect(&mf->lf);\r
1315 }\r
1316 \r
1317 // [HGM] This platform-dependent table provides the location for storing the color info\r
1318 void *\r
1319 colorVariable[] = {\r
1320   &whitePieceColor, \r
1321   &blackPieceColor, \r
1322   &lightSquareColor,\r
1323   &darkSquareColor, \r
1324   &highlightSquareColor,\r
1325   &premoveHighlightColor,\r
1326   NULL,\r
1327   &consoleBackgroundColor,\r
1328   &appData.fontForeColorWhite,\r
1329   &appData.fontBackColorWhite,\r
1330   &appData.fontForeColorBlack,\r
1331   &appData.fontBackColorBlack,\r
1332   &appData.evalHistColorWhite,\r
1333   &appData.evalHistColorBlack,\r
1334   &appData.highlightArrowColor,\r
1335 };\r
1336 \r
1337 /* Command line font name parser.  NULL name means do nothing.\r
1338    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1339    For backward compatibility, syntax without the colon is also\r
1340    accepted, but font names with digits in them won't work in that case.\r
1341 */\r
1342 VOID\r
1343 ParseFontName(char *name, MyFontParams *mfp)\r
1344 {\r
1345   char *p, *q;\r
1346   if (name == NULL) return;\r
1347   p = name;\r
1348   q = strchr(p, ':');\r
1349   if (q) {\r
1350     if (q - p >= sizeof(mfp->faceName))\r
1351       ExitArgError(_("Font name too long:"), name, TRUE);\r
1352     memcpy(mfp->faceName, p, q - p);\r
1353     mfp->faceName[q - p] = NULLCHAR;\r
1354     p = q + 1;\r
1355   } else {\r
1356     q = mfp->faceName;\r
1357 \r
1358     while (*p && !isdigit(*p)) {\r
1359       *q++ = *p++;\r
1360       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1361         ExitArgError(_("Font name too long:"), name, TRUE);\r
1362     }\r
1363     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1364     *q = NULLCHAR;\r
1365   }\r
1366   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1367   mfp->pointSize = (float) atof(p);\r
1368   mfp->bold = (strchr(p, 'b') != NULL);\r
1369   mfp->italic = (strchr(p, 'i') != NULL);\r
1370   mfp->underline = (strchr(p, 'u') != NULL);\r
1371   mfp->strikeout = (strchr(p, 's') != NULL);\r
1372   mfp->charset = DEFAULT_CHARSET;\r
1373   q = strchr(p, 'c');\r
1374   if (q)\r
1375     mfp->charset = (BYTE) atoi(q+1);\r
1376 }\r
1377 \r
1378 void\r
1379 ParseFont(char *name, int number)\r
1380 { // wrapper to shield back-end from 'font'\r
1381   ParseFontName(name, &font[boardSize][number]->mfp);\r
1382 }\r
1383 \r
1384 void\r
1385 SetFontDefaults()\r
1386 { // in WB  we have a 2D array of fonts; this initializes their description\r
1387   int i, j;\r
1388   /* Point font array elements to structures and\r
1389      parse default font names */\r
1390   for (i=0; i<NUM_FONTS; i++) {\r
1391     for (j=0; j<NUM_SIZES; j++) {\r
1392       font[j][i] = &fontRec[j][i];\r
1393       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1394     }\r
1395   }\r
1396 }\r
1397 \r
1398 void\r
1399 CreateFonts()\r
1400 { // here we create the actual fonts from the selected descriptions\r
1401   int i, j;\r
1402   for (i=0; i<NUM_FONTS; i++) {\r
1403     for (j=0; j<NUM_SIZES; j++) {\r
1404       CreateFontInMF(font[j][i]);\r
1405     }\r
1406   }\r
1407 }\r
1408 /* Color name parser.\r
1409    X version accepts X color names, but this one\r
1410    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1411 COLORREF\r
1412 ParseColorName(char *name)\r
1413 {\r
1414   int red, green, blue, count;\r
1415   char buf[MSG_SIZ];\r
1416 \r
1417   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1418   if (count != 3) {\r
1419     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1420       &red, &green, &blue);\r
1421   }\r
1422   if (count != 3) {\r
1423     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1424     DisplayError(buf, 0);\r
1425     return RGB(0, 0, 0);\r
1426   }\r
1427   return PALETTERGB(red, green, blue);\r
1428 }\r
1429 \r
1430 void\r
1431 ParseColor(int n, char *name)\r
1432 { // for WinBoard the color is an int, which needs to be derived from the string\r
1433   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1434 }\r
1435 \r
1436 void\r
1437 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1438 {\r
1439   char *e = argValue;\r
1440   int eff = 0;\r
1441 \r
1442   while (*e) {\r
1443     if (*e == 'b')      eff |= CFE_BOLD;\r
1444     else if (*e == 'i') eff |= CFE_ITALIC;\r
1445     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1446     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1447     else if (*e == '#' || isdigit(*e)) break;\r
1448     e++;\r
1449   }\r
1450   *effects = eff;\r
1451   *color   = ParseColorName(e);\r
1452 }\r
1453 \r
1454 void\r
1455 ParseTextAttribs(ColorClass cc, char *s)\r
1456 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1457     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1458     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1459 }\r
1460 \r
1461 void\r
1462 ParseBoardSize(void *addr, char *name)\r
1463 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1464   BoardSize bs = SizeTiny;\r
1465   while (sizeInfo[bs].name != NULL) {\r
1466     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1467         *(BoardSize *)addr = bs;\r
1468         return;\r
1469     }\r
1470     bs++;\r
1471   }\r
1472   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1473 }\r
1474 \r
1475 void\r
1476 LoadAllSounds()\r
1477 { // [HGM] import name from appData first\r
1478   ColorClass cc;\r
1479   SoundClass sc;\r
1480   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1481     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1482     textAttribs[cc].sound.data = NULL;\r
1483     MyLoadSound(&textAttribs[cc].sound);\r
1484   }\r
1485   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1486     textAttribs[cc].sound.name = strdup("");\r
1487     textAttribs[cc].sound.data = NULL;\r
1488   }\r
1489   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1490     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1491     sounds[sc].data = NULL;\r
1492     MyLoadSound(&sounds[sc]);\r
1493   }\r
1494 }\r
1495 \r
1496 void\r
1497 SetCommPortDefaults()\r
1498 {\r
1499    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1500   dcb.DCBlength = sizeof(DCB);\r
1501   dcb.BaudRate = 9600;\r
1502   dcb.fBinary = TRUE;\r
1503   dcb.fParity = FALSE;\r
1504   dcb.fOutxCtsFlow = FALSE;\r
1505   dcb.fOutxDsrFlow = FALSE;\r
1506   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1507   dcb.fDsrSensitivity = FALSE;\r
1508   dcb.fTXContinueOnXoff = TRUE;\r
1509   dcb.fOutX = FALSE;\r
1510   dcb.fInX = FALSE;\r
1511   dcb.fNull = FALSE;\r
1512   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1513   dcb.fAbortOnError = FALSE;\r
1514   dcb.ByteSize = 7;\r
1515   dcb.Parity = SPACEPARITY;\r
1516   dcb.StopBits = ONESTOPBIT;\r
1517 }\r
1518 \r
1519 // [HGM] args: these three cases taken out to stay in front-end\r
1520 void\r
1521 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1522 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1523         // while the curent board size determines the element. This system should be ported to XBoard.\r
1524         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1525         int bs;\r
1526         for (bs=0; bs<NUM_SIZES; bs++) {\r
1527           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1528           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1529           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1530             ad->argName, mfp->faceName, mfp->pointSize,\r
1531             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1532             mfp->bold ? "b" : "",\r
1533             mfp->italic ? "i" : "",\r
1534             mfp->underline ? "u" : "",\r
1535             mfp->strikeout ? "s" : "",\r
1536             (int)mfp->charset);\r
1537         }\r
1538       }\r
1539 \r
1540 void\r
1541 ExportSounds()\r
1542 { // [HGM] copy the names from the internal WB variables to appData\r
1543   ColorClass cc;\r
1544   SoundClass sc;\r
1545   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1546     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1547   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1548     (&appData.soundMove)[sc] = sounds[sc].name;\r
1549 }\r
1550 \r
1551 void\r
1552 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1553 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1554         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1555         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1556           (ta->effects & CFE_BOLD) ? "b" : "",\r
1557           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1558           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1559           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1560           (ta->effects) ? " " : "",\r
1561           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1562       }\r
1563 \r
1564 void\r
1565 SaveColor(FILE *f, ArgDescriptor *ad)\r
1566 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1567         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1568         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1569           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1570 }\r
1571 \r
1572 void\r
1573 SaveBoardSize(FILE *f, char *name, void *addr)\r
1574 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1575   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1576 }\r
1577 \r
1578 void\r
1579 ParseCommPortSettings(char *s)\r
1580 { // wrapper to keep dcb from back-end\r
1581   ParseCommSettings(s, &dcb);\r
1582 }\r
1583 \r
1584 void\r
1585 GetWindowCoords()\r
1586 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1587   GetActualPlacement(hwndMain, &wpMain);\r
1588   GetActualPlacement(hwndConsole, &wpConsole);\r
1589   GetActualPlacement(commentDialog, &wpComment);\r
1590   GetActualPlacement(editTagsDialog, &wpTags);\r
1591   GetActualPlacement(gameListDialog, &wpGameList);\r
1592   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1593   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1594   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1595 }\r
1596 \r
1597 void\r
1598 PrintCommPortSettings(FILE *f, char *name)\r
1599 { // wrapper to shield back-end from DCB\r
1600       PrintCommSettings(f, name, &dcb);\r
1601 }\r
1602 \r
1603 int\r
1604 MySearchPath(char *installDir, char *name, char *fullname)\r
1605 {\r
1606   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1607   if(name[0]== '%') {\r
1608     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1609     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1610       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1611       *strchr(buf, '%') = 0;\r
1612       strcat(fullname, getenv(buf));\r
1613       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1614     }\r
1615     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1616     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1617     return (int) strlen(fullname);\r
1618   }\r
1619   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1620 }\r
1621 \r
1622 int\r
1623 MyGetFullPathName(char *name, char *fullname)\r
1624 {\r
1625   char *dummy;\r
1626   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1627 }\r
1628 \r
1629 int\r
1630 MainWindowUp()\r
1631 { // [HGM] args: allows testing if main window is realized from back-end\r
1632   return hwndMain != NULL;\r
1633 }\r
1634 \r
1635 void\r
1636 PopUpStartupDialog()\r
1637 {\r
1638     FARPROC lpProc;\r
1639     \r
1640     LoadLanguageFile(appData.language);\r
1641     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1642     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1643     FreeProcInstance(lpProc);\r
1644 }\r
1645 \r
1646 /*---------------------------------------------------------------------------*\\r
1647  *\r
1648  * GDI board drawing routines\r
1649  *\r
1650 \*---------------------------------------------------------------------------*/\r
1651 \r
1652 /* [AS] Draw square using background texture */\r
1653 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1654 {\r
1655     XFORM   x;\r
1656 \r
1657     if( mode == 0 ) {\r
1658         return; /* Should never happen! */\r
1659     }\r
1660 \r
1661     SetGraphicsMode( dst, GM_ADVANCED );\r
1662 \r
1663     switch( mode ) {\r
1664     case 1:\r
1665         /* Identity */\r
1666         break;\r
1667     case 2:\r
1668         /* X reflection */\r
1669         x.eM11 = -1.0;\r
1670         x.eM12 = 0;\r
1671         x.eM21 = 0;\r
1672         x.eM22 = 1.0;\r
1673         x.eDx = (FLOAT) dw + dx - 1;\r
1674         x.eDy = 0;\r
1675         dx = 0;\r
1676         SetWorldTransform( dst, &x );\r
1677         break;\r
1678     case 3:\r
1679         /* Y reflection */\r
1680         x.eM11 = 1.0;\r
1681         x.eM12 = 0;\r
1682         x.eM21 = 0;\r
1683         x.eM22 = -1.0;\r
1684         x.eDx = 0;\r
1685         x.eDy = (FLOAT) dh + dy - 1;\r
1686         dy = 0;\r
1687         SetWorldTransform( dst, &x );\r
1688         break;\r
1689     case 4:\r
1690         /* X/Y flip */\r
1691         x.eM11 = 0;\r
1692         x.eM12 = 1.0;\r
1693         x.eM21 = 1.0;\r
1694         x.eM22 = 0;\r
1695         x.eDx = (FLOAT) dx;\r
1696         x.eDy = (FLOAT) dy;\r
1697         dx = 0;\r
1698         dy = 0;\r
1699         SetWorldTransform( dst, &x );\r
1700         break;\r
1701     }\r
1702 \r
1703     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1704 \r
1705     x.eM11 = 1.0;\r
1706     x.eM12 = 0;\r
1707     x.eM21 = 0;\r
1708     x.eM22 = 1.0;\r
1709     x.eDx = 0;\r
1710     x.eDy = 0;\r
1711     SetWorldTransform( dst, &x );\r
1712 \r
1713     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1714 }\r
1715 \r
1716 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1717 enum {\r
1718     PM_WP = (int) WhitePawn, \r
1719     PM_WN = (int) WhiteKnight, \r
1720     PM_WB = (int) WhiteBishop, \r
1721     PM_WR = (int) WhiteRook, \r
1722     PM_WQ = (int) WhiteQueen, \r
1723     PM_WF = (int) WhiteFerz, \r
1724     PM_WW = (int) WhiteWazir, \r
1725     PM_WE = (int) WhiteAlfil, \r
1726     PM_WM = (int) WhiteMan, \r
1727     PM_WO = (int) WhiteCannon, \r
1728     PM_WU = (int) WhiteUnicorn, \r
1729     PM_WH = (int) WhiteNightrider, \r
1730     PM_WA = (int) WhiteAngel, \r
1731     PM_WC = (int) WhiteMarshall, \r
1732     PM_WAB = (int) WhiteCardinal, \r
1733     PM_WD = (int) WhiteDragon, \r
1734     PM_WL = (int) WhiteLance, \r
1735     PM_WS = (int) WhiteCobra, \r
1736     PM_WV = (int) WhiteFalcon, \r
1737     PM_WSG = (int) WhiteSilver, \r
1738     PM_WG = (int) WhiteGrasshopper, \r
1739     PM_WK = (int) WhiteKing,\r
1740     PM_BP = (int) BlackPawn, \r
1741     PM_BN = (int) BlackKnight, \r
1742     PM_BB = (int) BlackBishop, \r
1743     PM_BR = (int) BlackRook, \r
1744     PM_BQ = (int) BlackQueen, \r
1745     PM_BF = (int) BlackFerz, \r
1746     PM_BW = (int) BlackWazir, \r
1747     PM_BE = (int) BlackAlfil, \r
1748     PM_BM = (int) BlackMan,\r
1749     PM_BO = (int) BlackCannon, \r
1750     PM_BU = (int) BlackUnicorn, \r
1751     PM_BH = (int) BlackNightrider, \r
1752     PM_BA = (int) BlackAngel, \r
1753     PM_BC = (int) BlackMarshall, \r
1754     PM_BG = (int) BlackGrasshopper, \r
1755     PM_BAB = (int) BlackCardinal,\r
1756     PM_BD = (int) BlackDragon,\r
1757     PM_BL = (int) BlackLance,\r
1758     PM_BS = (int) BlackCobra,\r
1759     PM_BV = (int) BlackFalcon,\r
1760     PM_BSG = (int) BlackSilver,\r
1761     PM_BK = (int) BlackKing\r
1762 };\r
1763 \r
1764 static HFONT hPieceFont = NULL;\r
1765 static HBITMAP hPieceMask[(int) EmptySquare];\r
1766 static HBITMAP hPieceFace[(int) EmptySquare];\r
1767 static int fontBitmapSquareSize = 0;\r
1768 static char pieceToFontChar[(int) EmptySquare] =\r
1769                               { 'p', 'n', 'b', 'r', 'q', \r
1770                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1771                       'k', 'o', 'm', 'v', 't', 'w', \r
1772                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1773                                                               'l' };\r
1774 \r
1775 extern BOOL SetCharTable( char *table, const char * map );\r
1776 /* [HGM] moved to backend.c */\r
1777 \r
1778 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1779 {\r
1780     HBRUSH hbrush;\r
1781     BYTE r1 = GetRValue( color );\r
1782     BYTE g1 = GetGValue( color );\r
1783     BYTE b1 = GetBValue( color );\r
1784     BYTE r2 = r1 / 2;\r
1785     BYTE g2 = g1 / 2;\r
1786     BYTE b2 = b1 / 2;\r
1787     RECT rc;\r
1788 \r
1789     /* Create a uniform background first */\r
1790     hbrush = CreateSolidBrush( color );\r
1791     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1792     FillRect( hdc, &rc, hbrush );\r
1793     DeleteObject( hbrush );\r
1794     \r
1795     if( mode == 1 ) {\r
1796         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1797         int steps = squareSize / 2;\r
1798         int i;\r
1799 \r
1800         for( i=0; i<steps; i++ ) {\r
1801             BYTE r = r1 - (r1-r2) * i / steps;\r
1802             BYTE g = g1 - (g1-g2) * i / steps;\r
1803             BYTE b = b1 - (b1-b2) * i / steps;\r
1804 \r
1805             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1806             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1807             FillRect( hdc, &rc, hbrush );\r
1808             DeleteObject(hbrush);\r
1809         }\r
1810     }\r
1811     else if( mode == 2 ) {\r
1812         /* Diagonal gradient, good more or less for every piece */\r
1813         POINT triangle[3];\r
1814         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1815         HBRUSH hbrush_old;\r
1816         int steps = squareSize;\r
1817         int i;\r
1818 \r
1819         triangle[0].x = squareSize - steps;\r
1820         triangle[0].y = squareSize;\r
1821         triangle[1].x = squareSize;\r
1822         triangle[1].y = squareSize;\r
1823         triangle[2].x = squareSize;\r
1824         triangle[2].y = squareSize - steps;\r
1825 \r
1826         for( i=0; i<steps; i++ ) {\r
1827             BYTE r = r1 - (r1-r2) * i / steps;\r
1828             BYTE g = g1 - (g1-g2) * i / steps;\r
1829             BYTE b = b1 - (b1-b2) * i / steps;\r
1830 \r
1831             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1832             hbrush_old = SelectObject( hdc, hbrush );\r
1833             Polygon( hdc, triangle, 3 );\r
1834             SelectObject( hdc, hbrush_old );\r
1835             DeleteObject(hbrush);\r
1836             triangle[0].x++;\r
1837             triangle[2].y++;\r
1838         }\r
1839 \r
1840         SelectObject( hdc, hpen );\r
1841     }\r
1842 }\r
1843 \r
1844 /*\r
1845     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1846     seems to work ok. The main problem here is to find the "inside" of a chess\r
1847     piece: follow the steps as explained below.\r
1848 */\r
1849 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1850 {\r
1851     HBITMAP hbm;\r
1852     HBITMAP hbm_old;\r
1853     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1854     RECT rc;\r
1855     SIZE sz;\r
1856 \r
1857 \r
1858     POINT pt;\r
1859     int backColor = whitePieceColor; \r
1860     int foreColor = blackPieceColor;\r
1861     \r
1862     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1863         backColor = appData.fontBackColorWhite;\r
1864         foreColor = appData.fontForeColorWhite;\r
1865     }\r
1866     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1867         backColor = appData.fontBackColorBlack;\r
1868         foreColor = appData.fontForeColorBlack;\r
1869     }\r
1870 \r
1871     /* Mask */\r
1872     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1873 \r
1874     hbm_old = SelectObject( hdc, hbm );\r
1875 \r
1876     rc.left = 0;\r
1877     rc.top = 0;\r
1878     rc.right = squareSize;\r
1879     rc.bottom = squareSize;\r
1880 \r
1881     /* Step 1: background is now black */\r
1882     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1883 \r
1884     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1885 \r
1886     pt.x = (squareSize - sz.cx) / 2;\r
1887     pt.y = (squareSize - sz.cy) / 2;\r
1888 \r
1889     SetBkMode( hdc, TRANSPARENT );\r
1890     SetTextColor( hdc, chroma );\r
1891     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1892     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1893 \r
1894     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1895     /* Step 3: the area outside the piece is filled with white */\r
1896 //    FloodFill( hdc, 0, 0, chroma );\r
1897     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1898     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1899     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1900     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1901     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1902     /* \r
1903         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1904         but if the start point is not inside the piece we're lost!\r
1905         There should be a better way to do this... if we could create a region or path\r
1906         from the fill operation we would be fine for example.\r
1907     */\r
1908 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1909     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1910 \r
1911     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1912         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1913         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1914 \r
1915         SelectObject( dc2, bm2 );\r
1916         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1917         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1918         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1919         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1920         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1921 \r
1922         DeleteDC( dc2 );\r
1923         DeleteObject( bm2 );\r
1924     }\r
1925 \r
1926     SetTextColor( hdc, 0 );\r
1927     /* \r
1928         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1929         draw the piece again in black for safety.\r
1930     */\r
1931     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1932 \r
1933     SelectObject( hdc, hbm_old );\r
1934 \r
1935     if( hPieceMask[index] != NULL ) {\r
1936         DeleteObject( hPieceMask[index] );\r
1937     }\r
1938 \r
1939     hPieceMask[index] = hbm;\r
1940 \r
1941     /* Face */\r
1942     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1943 \r
1944     SelectObject( hdc, hbm );\r
1945 \r
1946     {\r
1947         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1948         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1949         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1950 \r
1951         SelectObject( dc1, hPieceMask[index] );\r
1952         SelectObject( dc2, bm2 );\r
1953         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1954         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1955         \r
1956         /* \r
1957             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1958             the piece background and deletes (makes transparent) the rest.\r
1959             Thanks to that mask, we are free to paint the background with the greates\r
1960             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1961             We use this, to make gradients and give the pieces a "roundish" look.\r
1962         */\r
1963         SetPieceBackground( hdc, backColor, 2 );\r
1964         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1965 \r
1966         DeleteDC( dc2 );\r
1967         DeleteDC( dc1 );\r
1968         DeleteObject( bm2 );\r
1969     }\r
1970 \r
1971     SetTextColor( hdc, foreColor );\r
1972     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1973 \r
1974     SelectObject( hdc, hbm_old );\r
1975 \r
1976     if( hPieceFace[index] != NULL ) {\r
1977         DeleteObject( hPieceFace[index] );\r
1978     }\r
1979 \r
1980     hPieceFace[index] = hbm;\r
1981 }\r
1982 \r
1983 static int TranslatePieceToFontPiece( int piece )\r
1984 {\r
1985     switch( piece ) {\r
1986     case BlackPawn:\r
1987         return PM_BP;\r
1988     case BlackKnight:\r
1989         return PM_BN;\r
1990     case BlackBishop:\r
1991         return PM_BB;\r
1992     case BlackRook:\r
1993         return PM_BR;\r
1994     case BlackQueen:\r
1995         return PM_BQ;\r
1996     case BlackKing:\r
1997         return PM_BK;\r
1998     case WhitePawn:\r
1999         return PM_WP;\r
2000     case WhiteKnight:\r
2001         return PM_WN;\r
2002     case WhiteBishop:\r
2003         return PM_WB;\r
2004     case WhiteRook:\r
2005         return PM_WR;\r
2006     case WhiteQueen:\r
2007         return PM_WQ;\r
2008     case WhiteKing:\r
2009         return PM_WK;\r
2010 \r
2011     case BlackAngel:\r
2012         return PM_BA;\r
2013     case BlackMarshall:\r
2014         return PM_BC;\r
2015     case BlackFerz:\r
2016         return PM_BF;\r
2017     case BlackNightrider:\r
2018         return PM_BH;\r
2019     case BlackAlfil:\r
2020         return PM_BE;\r
2021     case BlackWazir:\r
2022         return PM_BW;\r
2023     case BlackUnicorn:\r
2024         return PM_BU;\r
2025     case BlackCannon:\r
2026         return PM_BO;\r
2027     case BlackGrasshopper:\r
2028         return PM_BG;\r
2029     case BlackMan:\r
2030         return PM_BM;\r
2031     case BlackSilver:\r
2032         return PM_BSG;\r
2033     case BlackLance:\r
2034         return PM_BL;\r
2035     case BlackFalcon:\r
2036         return PM_BV;\r
2037     case BlackCobra:\r
2038         return PM_BS;\r
2039     case BlackCardinal:\r
2040         return PM_BAB;\r
2041     case BlackDragon:\r
2042         return PM_BD;\r
2043 \r
2044     case WhiteAngel:\r
2045         return PM_WA;\r
2046     case WhiteMarshall:\r
2047         return PM_WC;\r
2048     case WhiteFerz:\r
2049         return PM_WF;\r
2050     case WhiteNightrider:\r
2051         return PM_WH;\r
2052     case WhiteAlfil:\r
2053         return PM_WE;\r
2054     case WhiteWazir:\r
2055         return PM_WW;\r
2056     case WhiteUnicorn:\r
2057         return PM_WU;\r
2058     case WhiteCannon:\r
2059         return PM_WO;\r
2060     case WhiteGrasshopper:\r
2061         return PM_WG;\r
2062     case WhiteMan:\r
2063         return PM_WM;\r
2064     case WhiteSilver:\r
2065         return PM_WSG;\r
2066     case WhiteLance:\r
2067         return PM_WL;\r
2068     case WhiteFalcon:\r
2069         return PM_WV;\r
2070     case WhiteCobra:\r
2071         return PM_WS;\r
2072     case WhiteCardinal:\r
2073         return PM_WAB;\r
2074     case WhiteDragon:\r
2075         return PM_WD;\r
2076     }\r
2077 \r
2078     return 0;\r
2079 }\r
2080 \r
2081 void CreatePiecesFromFont()\r
2082 {\r
2083     LOGFONT lf;\r
2084     HDC hdc_window = NULL;\r
2085     HDC hdc = NULL;\r
2086     HFONT hfont_old;\r
2087     int fontHeight;\r
2088     int i;\r
2089 \r
2090     if( fontBitmapSquareSize < 0 ) {\r
2091         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2092         return;\r
2093     }\r
2094 \r
2095     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2096             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2097         fontBitmapSquareSize = -1;\r
2098         return;\r
2099     }\r
2100 \r
2101     if( fontBitmapSquareSize != squareSize ) {\r
2102         hdc_window = GetDC( hwndMain );\r
2103         hdc = CreateCompatibleDC( hdc_window );\r
2104 \r
2105         if( hPieceFont != NULL ) {\r
2106             DeleteObject( hPieceFont );\r
2107         }\r
2108         else {\r
2109             for( i=0; i<=(int)BlackKing; i++ ) {\r
2110                 hPieceMask[i] = NULL;\r
2111                 hPieceFace[i] = NULL;\r
2112             }\r
2113         }\r
2114 \r
2115         fontHeight = 75;\r
2116 \r
2117         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2118             fontHeight = appData.fontPieceSize;\r
2119         }\r
2120 \r
2121         fontHeight = (fontHeight * squareSize) / 100;\r
2122 \r
2123         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2124         lf.lfWidth = 0;\r
2125         lf.lfEscapement = 0;\r
2126         lf.lfOrientation = 0;\r
2127         lf.lfWeight = FW_NORMAL;\r
2128         lf.lfItalic = 0;\r
2129         lf.lfUnderline = 0;\r
2130         lf.lfStrikeOut = 0;\r
2131         lf.lfCharSet = DEFAULT_CHARSET;\r
2132         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2133         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2134         lf.lfQuality = PROOF_QUALITY;\r
2135         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2136         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2137         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2138 \r
2139         hPieceFont = CreateFontIndirect( &lf );\r
2140 \r
2141         if( hPieceFont == NULL ) {\r
2142             fontBitmapSquareSize = -2;\r
2143         }\r
2144         else {\r
2145             /* Setup font-to-piece character table */\r
2146             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2147                 /* No (or wrong) global settings, try to detect the font */\r
2148                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2149                     /* Alpha */\r
2150                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2151                 }\r
2152                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2153                     /* DiagramTT* family */\r
2154                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2155                 }\r
2156                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2157                     /* Fairy symbols */\r
2158                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2159                 }\r
2160                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2161                     /* Good Companion (Some characters get warped as literal :-( */\r
2162                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2163                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2164                     SetCharTable(pieceToFontChar, s);\r
2165                 }\r
2166                 else {\r
2167                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2168                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2169                 }\r
2170             }\r
2171 \r
2172             /* Create bitmaps */\r
2173             hfont_old = SelectObject( hdc, hPieceFont );\r
2174             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2175                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2176                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2177 \r
2178             SelectObject( hdc, hfont_old );\r
2179 \r
2180             fontBitmapSquareSize = squareSize;\r
2181         }\r
2182     }\r
2183 \r
2184     if( hdc != NULL ) {\r
2185         DeleteDC( hdc );\r
2186     }\r
2187 \r
2188     if( hdc_window != NULL ) {\r
2189         ReleaseDC( hwndMain, hdc_window );\r
2190     }\r
2191 }\r
2192 \r
2193 HBITMAP\r
2194 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2195 {\r
2196   char name[128], buf[MSG_SIZ];\r
2197 \r
2198     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2199   if(appData.pieceDirectory[0]) {\r
2200     HBITMAP res;\r
2201     snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);\r
2202     res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
2203     if(res) return res;\r
2204   }\r
2205   if (gameInfo.event &&\r
2206       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2207       strcmp(name, "k80s") == 0) {\r
2208     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2209   }\r
2210   return LoadBitmap(hinst, name);\r
2211 }\r
2212 \r
2213 \r
2214 /* Insert a color into the program's logical palette\r
2215    structure.  This code assumes the given color is\r
2216    the result of the RGB or PALETTERGB macro, and it\r
2217    knows how those macros work (which is documented).\r
2218 */\r
2219 VOID\r
2220 InsertInPalette(COLORREF color)\r
2221 {\r
2222   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2223 \r
2224   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2225     DisplayFatalError(_("Too many colors"), 0, 1);\r
2226     pLogPal->palNumEntries--;\r
2227     return;\r
2228   }\r
2229 \r
2230   pe->peFlags = (char) 0;\r
2231   pe->peRed = (char) (0xFF & color);\r
2232   pe->peGreen = (char) (0xFF & (color >> 8));\r
2233   pe->peBlue = (char) (0xFF & (color >> 16));\r
2234   return;\r
2235 }\r
2236 \r
2237 \r
2238 VOID\r
2239 InitDrawingColors()\r
2240 {\r
2241   int i;\r
2242   if (pLogPal == NULL) {\r
2243     /* Allocate enough memory for a logical palette with\r
2244      * PALETTESIZE entries and set the size and version fields\r
2245      * of the logical palette structure.\r
2246      */\r
2247     pLogPal = (NPLOGPALETTE)\r
2248       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2249                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2250     pLogPal->palVersion    = 0x300;\r
2251   }\r
2252   pLogPal->palNumEntries = 0;\r
2253 \r
2254   InsertInPalette(lightSquareColor);\r
2255   InsertInPalette(darkSquareColor);\r
2256   InsertInPalette(whitePieceColor);\r
2257   InsertInPalette(blackPieceColor);\r
2258   InsertInPalette(highlightSquareColor);\r
2259   InsertInPalette(premoveHighlightColor);\r
2260 \r
2261   /*  create a logical color palette according the information\r
2262    *  in the LOGPALETTE structure.\r
2263    */\r
2264   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2265 \r
2266   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2267   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2268   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2269   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2270   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2271   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2272   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2273     for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers\r
2274 \r
2275    /* [AS] Force rendering of the font-based pieces */\r
2276   if( fontBitmapSquareSize > 0 ) {\r
2277     fontBitmapSquareSize = 0;\r
2278   }\r
2279 }\r
2280 \r
2281 \r
2282 int\r
2283 BoardWidth(int boardSize, int n)\r
2284 { /* [HGM] argument n added to allow different width and height */\r
2285   int lineGap = sizeInfo[boardSize].lineGap;\r
2286 \r
2287   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2288       lineGap = appData.overrideLineGap;\r
2289   }\r
2290 \r
2291   return (n + 1) * lineGap +\r
2292           n * sizeInfo[boardSize].squareSize;\r
2293 }\r
2294 \r
2295 /* Respond to board resize by dragging edge */\r
2296 VOID\r
2297 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2298 {\r
2299   BoardSize newSize = NUM_SIZES - 1;\r
2300   static int recurse = 0;\r
2301   if (IsIconic(hwndMain)) return;\r
2302   if (recurse > 0) return;\r
2303   recurse++;\r
2304   while (newSize > 0) {\r
2305         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2306         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2307            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2308     newSize--;\r
2309   } \r
2310   boardSize = newSize;\r
2311   InitDrawingSizes(boardSize, flags);\r
2312   recurse--;\r
2313 }\r
2314 \r
2315 \r
2316 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2317 \r
2318 VOID\r
2319 InitDrawingSizes(BoardSize boardSize, int flags)\r
2320 {\r
2321   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2322   ChessSquare piece;\r
2323   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2324   HDC hdc;\r
2325   SIZE clockSize, messageSize;\r
2326   HFONT oldFont;\r
2327   char buf[MSG_SIZ];\r
2328   char *str;\r
2329   HMENU hmenu = GetMenu(hwndMain);\r
2330   RECT crect, wrect, oldRect;\r
2331   int offby;\r
2332   LOGBRUSH logbrush;\r
2333   VariantClass v = gameInfo.variant;\r
2334 \r
2335   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2336   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2337 \r
2338   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2339   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2340   if(boardSize == -1) return;     // no size defined yet; abort (to allow early call of InitPosition)\r
2341   oldBoardSize = boardSize;\r
2342 \r
2343   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2344   { // correct board size to one where built-in pieces exist\r
2345     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2346        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2347 \r
2348       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2349       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2350       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy || v == VariantLion ) {\r
2351       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2352       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2353                                    boardSize = SizeMiddling;\r
2354     }\r
2355   }\r
2356   if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2357 \r
2358   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2359   oldRect.top = wpMain.y;\r
2360   oldRect.right = wpMain.x + wpMain.width;\r
2361   oldRect.bottom = wpMain.y + wpMain.height;\r
2362 \r
2363   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2364   smallLayout = sizeInfo[boardSize].smallLayout;\r
2365   squareSize = sizeInfo[boardSize].squareSize;\r
2366   lineGap = sizeInfo[boardSize].lineGap;\r
2367   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2368   border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;\r
2369 \r
2370   // [HGM] decide on tininess based on total board width rather than square size\r
2371   tinyLayout = squareSize * (BOARD_WIDTH);\r
2372   tinyLayout = tinyLayout < 35*8 ? 2 : tinyLayout < 43*8 ? 1 : 0;\r
2373 \r
2374   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2375       lineGap = appData.overrideLineGap;\r
2376   }\r
2377 \r
2378   if (tinyLayout != oldTinyLayout) {\r
2379     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2380     if (tinyLayout == 2) {\r
2381       style &= ~WS_SYSMENU;\r
2382       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2383                  "&Minimize\tCtrl+F4");\r
2384     } else {\r
2385       style |= WS_SYSMENU;\r
2386       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2387     }\r
2388     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2389 \r
2390     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2391       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2392         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2393     }\r
2394     DrawMenuBar(hwndMain);\r
2395   }\r
2396 \r
2397   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;\r
2398   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;\r
2399 \r
2400   /* Get text area sizes */\r
2401   hdc = GetDC(hwndMain);\r
2402   if (appData.clockMode) {\r
2403     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2404   } else {\r
2405     snprintf(buf, MSG_SIZ, _("White"));\r
2406   }\r
2407   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2408   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2409   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2410   str = _("We only care about the height here");\r
2411   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2412   SelectObject(hdc, oldFont);\r
2413   ReleaseDC(hwndMain, hdc);\r
2414 \r
2415   /* Compute where everything goes */\r
2416   if((first.programLogo || second.programLogo) && tinyLayout != 2) {\r
2417         /* [HGM] logo: if either logo is on, reserve space for it */\r
2418         logoHeight =  2*clockSize.cy;\r
2419         leftLogoRect.left   = OUTER_MARGIN;\r
2420         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2421         leftLogoRect.top    = OUTER_MARGIN;\r
2422         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2423 \r
2424         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2425         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2426         rightLogoRect.top    = OUTER_MARGIN;\r
2427         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2428 \r
2429 \r
2430     whiteRect.left = leftLogoRect.right;\r
2431     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2432     whiteRect.top = OUTER_MARGIN;\r
2433     whiteRect.bottom = whiteRect.top + logoHeight;\r
2434 \r
2435     blackRect.right = rightLogoRect.left;\r
2436     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2437     blackRect.top = whiteRect.top;\r
2438     blackRect.bottom = whiteRect.bottom;\r
2439   } else {\r
2440     whiteRect.left = OUTER_MARGIN;\r
2441     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2442     whiteRect.top = OUTER_MARGIN;\r
2443     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2444 \r
2445     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2446     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2447     blackRect.top = whiteRect.top;\r
2448     blackRect.bottom = whiteRect.bottom;\r
2449 \r
2450     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2451   }\r
2452 \r
2453   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2454   if (appData.showButtonBar) {\r
2455     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2456       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2457   } else {\r
2458     messageRect.right = OUTER_MARGIN + boardWidth;\r
2459   }\r
2460   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2461   messageRect.bottom = messageRect.top + messageSize.cy;\r
2462 \r
2463   boardRect.left = OUTER_MARGIN;\r
2464   boardRect.right = boardRect.left + boardWidth;\r
2465   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2466   boardRect.bottom = boardRect.top + boardHeight;\r
2467 \r
2468   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2469   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2470   oldTinyLayout = tinyLayout;\r
2471   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2472   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2473     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2474   winW *= 1 + twoBoards;\r
2475   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2476   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2477   wpMain.height = winH; //       without disturbing window attachments\r
2478   GetWindowRect(hwndMain, &wrect);\r
2479   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2480                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2481 \r
2482   // [HGM] placement: let attached windows follow size change.\r
2483   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2484   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2485   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2486   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2487   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2488 \r
2489   /* compensate if menu bar wrapped */\r
2490   GetClientRect(hwndMain, &crect);\r
2491   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2492   wpMain.height += offby;\r
2493   switch (flags) {\r
2494   case WMSZ_TOPLEFT:\r
2495     SetWindowPos(hwndMain, NULL, \r
2496                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2497                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2498     break;\r
2499 \r
2500   case WMSZ_TOPRIGHT:\r
2501   case WMSZ_TOP:\r
2502     SetWindowPos(hwndMain, NULL, \r
2503                  wrect.left, wrect.bottom - wpMain.height, \r
2504                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2505     break;\r
2506 \r
2507   case WMSZ_BOTTOMLEFT:\r
2508   case WMSZ_LEFT:\r
2509     SetWindowPos(hwndMain, NULL, \r
2510                  wrect.right - wpMain.width, wrect.top, \r
2511                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2512     break;\r
2513 \r
2514   case WMSZ_BOTTOMRIGHT:\r
2515   case WMSZ_BOTTOM:\r
2516   case WMSZ_RIGHT:\r
2517   default:\r
2518     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2519                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2520     break;\r
2521   }\r
2522 \r
2523   hwndPause = NULL;\r
2524   for (i = 0; i < N_BUTTONS; i++) {\r
2525     if (buttonDesc[i].hwnd != NULL) {\r
2526       DestroyWindow(buttonDesc[i].hwnd);\r
2527       buttonDesc[i].hwnd = NULL;\r
2528     }\r
2529     if (appData.showButtonBar) {\r
2530       buttonDesc[i].hwnd =\r
2531         CreateWindow("BUTTON", buttonDesc[i].label,\r
2532                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2533                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2534                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2535                      (HMENU) buttonDesc[i].id,\r
2536                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2537       if (tinyLayout == 2) {\r
2538         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2539                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2540                     MAKELPARAM(FALSE, 0));\r
2541       }\r
2542       if (buttonDesc[i].id == IDM_Pause)\r
2543         hwndPause = buttonDesc[i].hwnd;\r
2544       buttonDesc[i].wndproc = (WNDPROC)\r
2545         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2546     }\r
2547   }\r
2548   if (gridPen != NULL) DeleteObject(gridPen);\r
2549   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2550   if (premovePen != NULL) DeleteObject(premovePen);\r
2551   if (lineGap != 0) {\r
2552     logbrush.lbStyle = BS_SOLID;\r
2553     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2554     gridPen =\r
2555       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2556                    lineGap, &logbrush, 0, NULL);\r
2557     logbrush.lbColor = highlightSquareColor;\r
2558     highlightPen =\r
2559       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2560                    lineGap, &logbrush, 0, NULL);\r
2561 \r
2562     logbrush.lbColor = premoveHighlightColor; \r
2563     premovePen =\r
2564       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2565                    lineGap, &logbrush, 0, NULL);\r
2566 \r
2567     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2568     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2569       gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;\r
2570       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2571         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2572       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2573         BOARD_WIDTH * (squareSize + lineGap) + border;\r
2574       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2575     }\r
2576     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2577       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;\r
2578       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2579         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2580         lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2581       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2582         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;\r
2583       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2584     }\r
2585   }\r
2586 \r
2587   /* [HGM] Licensing requirement */\r
2588 #ifdef GOTHIC\r
2589   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2590 #endif\r
2591 #ifdef FALCON\r
2592   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2593 #endif\r
2594   GothicPopUp( "", VariantNormal);\r
2595 \r
2596 \r
2597 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2598 \r
2599   /* Load piece bitmaps for this board size */\r
2600   for (i=0; i<=2; i++) {\r
2601     for (piece = WhitePawn;\r
2602          (int) piece < (int) BlackPawn;\r
2603          piece = (ChessSquare) ((int) piece + 1)) {\r
2604       if (pieceBitmap[i][piece] != NULL)\r
2605         DeleteObject(pieceBitmap[i][piece]);\r
2606       pieceBitmap[i][piece] = NULL;\r
2607     }\r
2608   }\r
2609 \r
2610   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2611 \r
2612   // Orthodox Chess pieces\r
2613   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2614   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2615   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2616   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2617   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2618   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2619   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2620   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2621   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2622   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2623   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2624   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2625   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2626   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2627   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2628   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2629     // in Shogi, Hijack the unused Queen for Lance\r
2630     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2631     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2632     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2633   } else {\r
2634     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2635     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2636     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2637   }\r
2638 \r
2639   if(squareSize <= 72 && squareSize >= 33) { \r
2640     /* A & C are available in most sizes now */\r
2641     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2642       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2643       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2644       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2645       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2646       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2647       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2648       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2649       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2650       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2651       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2652       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2653       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2654     } else { // Smirf-like\r
2655       if(gameInfo.variant == VariantSChess) {\r
2656         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2657         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2658         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2659       } else {\r
2660         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2661         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2662         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2663       }\r
2664     }\r
2665     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2666       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2667       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2668       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2669     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2670       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2671       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2672       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2673     } else { // WinBoard standard\r
2674       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2675       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2676       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2677     }\r
2678   }\r
2679 \r
2680 \r
2681   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2682     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2683     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2684     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2685     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2686     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2687     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2688     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2689     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2690     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2691     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2692     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2693     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2694     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2695     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2696     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2697     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2698     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2699     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2700     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2701     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2702     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2703     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2704     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2705     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2706     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2707     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2708     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2709     pieceBitmap[0][WhiteAmazon] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2710     pieceBitmap[1][WhiteAmazon] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2711     pieceBitmap[2][WhiteAmazon] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2712     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2713     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2714     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2715     pieceBitmap[0][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "s");\r
2716     pieceBitmap[1][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "o");\r
2717     pieceBitmap[2][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "w");\r
2718     pieceBitmap[0][WhiteCub] = DoLoadBitmap(hInst, "ln", squareSize, "s");\r
2719     pieceBitmap[1][WhiteCub] = DoLoadBitmap(hInst, "ln", squareSize, "o");\r
2720     pieceBitmap[2][WhiteCub] = DoLoadBitmap(hInst, "ln", squareSize, "w");\r
2721     pieceBitmap[0][WhiteWolf] = DoLoadBitmap(hInst, "wolf", squareSize, "s");\r
2722     pieceBitmap[1][WhiteWolf] = DoLoadBitmap(hInst, "wolf", squareSize, "o");\r
2723     pieceBitmap[2][WhiteWolf] = DoLoadBitmap(hInst, "wolf", squareSize, "w");\r
2724     pieceBitmap[0][WhiteCamel] = DoLoadBitmap(hInst, "camel", squareSize, "s");\r
2725     pieceBitmap[1][WhiteCamel] = DoLoadBitmap(hInst, "camel", squareSize, "o");\r
2726     pieceBitmap[2][WhiteCamel] = DoLoadBitmap(hInst, "camel", squareSize, "w");\r
2727     pieceBitmap[0][WhiteZebra] = DoLoadBitmap(hInst, "zebra", squareSize, "s");\r
2728     pieceBitmap[1][WhiteZebra] = DoLoadBitmap(hInst, "zebra", squareSize, "o");\r
2729     pieceBitmap[2][WhiteZebra] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2730 \r
2731     if(gameInfo.variant == VariantShogi && BOARD_HEIGHT != 7) { /* promoted Gold representations (but not in Tori!)*/\r
2732       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2733       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2734       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2735       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2736       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2737       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2738       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2739       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2740       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2741       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2742       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2743       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2744     } else {\r
2745       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2746       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2747       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2748       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2749       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2750       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2751       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2752       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2753       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2754       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2755       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2756       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2757     }\r
2758 \r
2759   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2760     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2761     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2762     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2763     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2764     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2765     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2766     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2767     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2768     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2769     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2770     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2771     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2772     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2773     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2774   }\r
2775 \r
2776 \r
2777   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2778   /* special Shogi support in this size */\r
2779   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2780       for (piece = WhitePawn;\r
2781            (int) piece < (int) BlackPawn;\r
2782            piece = (ChessSquare) ((int) piece + 1)) {\r
2783         if (pieceBitmap[i][piece] != NULL)\r
2784           DeleteObject(pieceBitmap[i][piece]);\r
2785       }\r
2786     }\r
2787   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2788   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2789   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2790   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2791   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2792   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2793   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2794   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2795   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2796   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2797   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2798   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2799   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2800   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2801   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2802   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2803   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2804   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2805   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2806   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2807   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2808   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2809   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2810   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2811   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2812   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2813   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2814   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2815   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2816   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2817   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2818   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2819   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2820   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2821   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2822   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2823   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2824   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2825   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2826   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2827   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2828   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2829   minorSize = 0;\r
2830   }\r
2831 \r
2832   if(appData.pieceDirectory[0]) for(i=WhitePawn; i<BlackPawn; i++) { // try for all missing pieces with new naming convention\r
2833     char buf[MSG_SIZ];\r
2834     if(pieceBitmap[0][i]) continue;\r
2835     snprintf(buf, MSG_SIZ, "piece%d_", i);\r
2836     pieceBitmap[0][i] = DoLoadBitmap(hInst, buf, squareSize, "s");\r
2837     pieceBitmap[1][i] = DoLoadBitmap(hInst, buf, squareSize, "o");\r
2838     pieceBitmap[2][i] = DoLoadBitmap(hInst, buf, squareSize, "w");\r
2839   }\r
2840 }\r
2841 \r
2842 HBITMAP\r
2843 PieceBitmap(ChessSquare p, int kind)\r
2844 {\r
2845   if ((int) p >= (int) BlackPawn)\r
2846     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2847 \r
2848   return pieceBitmap[kind][(int) p];\r
2849 }\r
2850 \r
2851 /***************************************************************/\r
2852 \r
2853 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2854 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2855 /*\r
2856 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2857 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2858 */\r
2859 \r
2860 VOID\r
2861 SquareToPos(int row, int column, int * x, int * y)\r
2862 {\r
2863   if (flipView) {\r
2864     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
2865     *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;\r
2866   } else {\r
2867     *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;\r
2868     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
2869   }\r
2870 }\r
2871 \r
2872 VOID\r
2873 DrawCoordsOnDC(HDC hdc)\r
2874 {\r
2875   static char files[] = "0123456789012345678901221098765432109876543210";\r
2876   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2877   char str[2] = { NULLCHAR, NULLCHAR };\r
2878   int oldMode, oldAlign, x, y, start, i;\r
2879   HFONT oldFont;\r
2880   HBRUSH oldBrush;\r
2881 \r
2882   if (!appData.showCoords)\r
2883     return;\r
2884 \r
2885   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2886 \r
2887   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2888   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2889   oldAlign = GetTextAlign(hdc);\r
2890   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2891 \r
2892   y = boardRect.top + lineGap;\r
2893   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2894 \r
2895   if(border) {\r
2896     SetTextAlign(hdc, TA_RIGHT|TA_TOP);\r
2897     x += border - lineGap - 4; y += squareSize - 6;\r
2898   } else\r
2899   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2900   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2901     str[0] = files[start + i];\r
2902     ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);\r
2903     y += squareSize + lineGap;\r
2904   }\r
2905 \r
2906   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2907 \r
2908   if(border) {\r
2909     SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2910     x += -border + 4; y += border - squareSize + 6;\r
2911   } else\r
2912   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2913   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2914     str[0] = ranks[start + i];\r
2915     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2916     x += squareSize + lineGap;\r
2917   }    \r
2918 \r
2919   SelectObject(hdc, oldBrush);\r
2920   SetBkMode(hdc, oldMode);\r
2921   SetTextAlign(hdc, oldAlign);\r
2922   SelectObject(hdc, oldFont);\r
2923 }\r
2924 \r
2925 VOID\r
2926 DrawGridOnDC(HDC hdc)\r
2927 {\r
2928   HPEN oldPen;\r
2929  \r
2930   if (lineGap != 0) {\r
2931     oldPen = SelectObject(hdc, gridPen);\r
2932     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2933     SelectObject(hdc, oldPen);\r
2934   }\r
2935 }\r
2936 \r
2937 #define HIGHLIGHT_PEN 0\r
2938 #define PREMOVE_PEN   1\r
2939 \r
2940 VOID\r
2941 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2942 {\r
2943   int x1, y1;\r
2944   HPEN oldPen, hPen;\r
2945   if (lineGap == 0) return;\r
2946   if (flipView) {\r
2947     x1 = boardRect.left +\r
2948       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;\r
2949     y1 = boardRect.top +\r
2950       lineGap/2 + y * (squareSize + lineGap) + border;\r
2951   } else {\r
2952     x1 = boardRect.left +\r
2953       lineGap/2 + x * (squareSize + lineGap) + border;\r
2954     y1 = boardRect.top +\r
2955       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;\r
2956   }\r
2957   hPen = pen ? premovePen : highlightPen;\r
2958   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2959   MoveToEx(hdc, x1, y1, NULL);\r
2960   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2961   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2962   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2963   LineTo(hdc, x1, y1);\r
2964   SelectObject(hdc, oldPen);\r
2965 }\r
2966 \r
2967 VOID\r
2968 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2969 {\r
2970   int i;\r
2971   for (i=0; i<2; i++) {\r
2972     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2973       DrawHighlightOnDC(hdc, TRUE,\r
2974                         h->sq[i].x, h->sq[i].y,\r
2975                         pen);\r
2976   }\r
2977 }\r
2978 \r
2979 /* Note: sqcolor is used only in monoMode */\r
2980 /* Note that this code is largely duplicated in woptions.c,\r
2981    function DrawSampleSquare, so that needs to be updated too */\r
2982 VOID\r
2983 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2984 {\r
2985   HBITMAP oldBitmap;\r
2986   HBRUSH oldBrush;\r
2987   int tmpSize;\r
2988 \r
2989   if (appData.blindfold) return;\r
2990 \r
2991   /* [AS] Use font-based pieces if needed */\r
2992   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2993     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2994     CreatePiecesFromFont();\r
2995 \r
2996     if( fontBitmapSquareSize == squareSize ) {\r
2997         int index = TranslatePieceToFontPiece(piece);\r
2998 \r
2999         SelectObject( tmphdc, hPieceMask[ index ] );\r
3000 \r
3001       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
3002         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
3003       else\r
3004         BitBlt( hdc,\r
3005             x, y,\r
3006             squareSize, squareSize,\r
3007             tmphdc,\r
3008             0, 0,\r
3009             SRCAND );\r
3010 \r
3011         SelectObject( tmphdc, hPieceFace[ index ] );\r
3012 \r
3013       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
3014         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
3015       else\r
3016         BitBlt( hdc,\r
3017             x, y,\r
3018             squareSize, squareSize,\r
3019             tmphdc,\r
3020             0, 0,\r
3021             SRCPAINT );\r
3022 \r
3023         return;\r
3024     }\r
3025   }\r
3026 \r
3027   if (appData.monoMode) {\r
3028     SelectObject(tmphdc, PieceBitmap(piece, \r
3029       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3030     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3031            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3032   } else {\r
3033     HBRUSH xBrush = whitePieceBrush;\r
3034     tmpSize = squareSize;\r
3035     if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);\r
3036     if(minorSize &&\r
3037         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3038          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3039       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3040       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3041       x += (squareSize - minorSize)>>1;\r
3042       y += squareSize - minorSize - 2;\r
3043       tmpSize = minorSize;\r
3044     }\r
3045     if (color || appData.allWhite ) {\r
3046       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3047       if( color )\r
3048               oldBrush = SelectObject(hdc, xBrush);\r
3049       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3050       if(appData.upsideDown && color==flipView)\r
3051         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3052       else\r
3053         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3054       /* Use black for outline of white pieces */\r
3055       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3056       if(appData.upsideDown && color==flipView)\r
3057         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3058       else\r
3059         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3060     } else if(appData.pieceDirectory[0]) {\r
3061       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3062       oldBrush = SelectObject(hdc, xBrush);\r
3063       if(appData.upsideDown && color==flipView)\r
3064         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3065       else\r
3066         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3067       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3068       if(appData.upsideDown && color==flipView)\r
3069         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3070       else\r
3071         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3072     } else {\r
3073       /* Use square color for details of black pieces */\r
3074       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3075       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3076       if(appData.upsideDown && !flipView)\r
3077         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3078       else\r
3079         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3080     }\r
3081     SelectObject(hdc, oldBrush);\r
3082     SelectObject(tmphdc, oldBitmap);\r
3083   }\r
3084 }\r
3085 \r
3086 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3087 int GetBackTextureMode( int algo )\r
3088 {\r
3089     int result = BACK_TEXTURE_MODE_DISABLED;\r
3090 \r
3091     switch( algo ) \r
3092     {\r
3093         case BACK_TEXTURE_MODE_PLAIN:\r
3094             result = 1; /* Always use identity map */\r
3095             break;\r
3096         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3097             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3098             break;\r
3099     }\r
3100 \r
3101     return result;\r
3102 }\r
3103 \r
3104 /* \r
3105     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3106     to handle redraws cleanly (as random numbers would always be different).\r
3107 */\r
3108 VOID RebuildTextureSquareInfo()\r
3109 {\r
3110     BITMAP bi;\r
3111     int lite_w = 0;\r
3112     int lite_h = 0;\r
3113     int dark_w = 0;\r
3114     int dark_h = 0;\r
3115     int row;\r
3116     int col;\r
3117 \r
3118     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3119 \r
3120     if( liteBackTexture != NULL ) {\r
3121         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3122             lite_w = bi.bmWidth;\r
3123             lite_h = bi.bmHeight;\r
3124         }\r
3125     }\r
3126 \r
3127     if( darkBackTexture != NULL ) {\r
3128         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3129             dark_w = bi.bmWidth;\r
3130             dark_h = bi.bmHeight;\r
3131         }\r
3132     }\r
3133 \r
3134     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3135         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3136             if( (col + row) & 1 ) {\r
3137                 /* Lite square */\r
3138                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3139                   if( lite_w >= squareSize*BOARD_WIDTH )\r
3140                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
3141                   else\r
3142                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3143                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
3144                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
3145                   else\r
3146                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3147                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3148                 }\r
3149             }\r
3150             else {\r
3151                 /* Dark square */\r
3152                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3153                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3154                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3155                   else\r
3156                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3157                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3158                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3159                   else\r
3160                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3161                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3162                 }\r
3163             }\r
3164         }\r
3165     }\r
3166 }\r
3167 \r
3168 /* [AS] Arrow highlighting support */\r
3169 \r
3170 static double A_WIDTH = 5; /* Width of arrow body */\r
3171 \r
3172 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3173 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3174 \r
3175 static double Sqr( double x )\r
3176 {\r
3177     return x*x;\r
3178 }\r
3179 \r
3180 static int Round( double x )\r
3181 {\r
3182     return (int) (x + 0.5);\r
3183 }\r
3184 \r
3185 /* Draw an arrow between two points using current settings */\r
3186 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3187 {\r
3188     POINT arrow[7];\r
3189     double dx, dy, j, k, x, y;\r
3190 \r
3191     if( d_x == s_x ) {\r
3192         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3193 \r
3194         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3195         arrow[0].y = s_y;\r
3196 \r
3197         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3198         arrow[1].y = d_y - h;\r
3199 \r
3200         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3201         arrow[2].y = d_y - h;\r
3202 \r
3203         arrow[3].x = d_x;\r
3204         arrow[3].y = d_y;\r
3205 \r
3206         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3207         arrow[5].y = d_y - h;\r
3208 \r
3209         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3210         arrow[4].y = d_y - h;\r
3211 \r
3212         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3213         arrow[6].y = s_y;\r
3214     }\r
3215     else if( d_y == s_y ) {\r
3216         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3217 \r
3218         arrow[0].x = s_x;\r
3219         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3220 \r
3221         arrow[1].x = d_x - w;\r
3222         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3223 \r
3224         arrow[2].x = d_x - w;\r
3225         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3226 \r
3227         arrow[3].x = d_x;\r
3228         arrow[3].y = d_y;\r
3229 \r
3230         arrow[5].x = d_x - w;\r
3231         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3232 \r
3233         arrow[4].x = d_x - w;\r
3234         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3235 \r
3236         arrow[6].x = s_x;\r
3237         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3238     }\r
3239     else {\r
3240         /* [AS] Needed a lot of paper for this! :-) */\r
3241         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3242         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3243   \r
3244         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3245 \r
3246         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3247 \r
3248         x = s_x;\r
3249         y = s_y;\r
3250 \r
3251         arrow[0].x = Round(x - j);\r
3252         arrow[0].y = Round(y + j*dx);\r
3253 \r
3254         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3255         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3256 \r
3257         if( d_x > s_x ) {\r
3258             x = (double) d_x - k;\r
3259             y = (double) d_y - k*dy;\r
3260         }\r
3261         else {\r
3262             x = (double) d_x + k;\r
3263             y = (double) d_y + k*dy;\r
3264         }\r
3265 \r
3266         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3267 \r
3268         arrow[6].x = Round(x - j);\r
3269         arrow[6].y = Round(y + j*dx);\r
3270 \r
3271         arrow[2].x = Round(arrow[6].x + 2*j);\r
3272         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3273 \r
3274         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3275         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3276 \r
3277         arrow[4].x = d_x;\r
3278         arrow[4].y = d_y;\r
3279 \r
3280         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3281         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3282     }\r
3283 \r
3284     Polygon( hdc, arrow, 7 );\r
3285 }\r
3286 \r
3287 /* [AS] Draw an arrow between two squares */\r
3288 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3289 {\r
3290     int s_x, s_y, d_x, d_y;\r
3291     HPEN hpen;\r
3292     HPEN holdpen;\r
3293     HBRUSH hbrush;\r
3294     HBRUSH holdbrush;\r
3295     LOGBRUSH stLB;\r
3296 \r
3297     if( s_col == d_col && s_row == d_row ) {\r
3298         return;\r
3299     }\r
3300 \r
3301     /* Get source and destination points */\r
3302     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3303     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3304 \r
3305     if( d_y > s_y ) {\r
3306         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3307     }\r
3308     else if( d_y < s_y ) {\r
3309         d_y += squareSize / 2 + squareSize / 4;\r
3310     }\r
3311     else {\r
3312         d_y += squareSize / 2;\r
3313     }\r
3314 \r
3315     if( d_x > s_x ) {\r
3316         d_x += squareSize / 2 - squareSize / 4;\r
3317     }\r
3318     else if( d_x < s_x ) {\r
3319         d_x += squareSize / 2 + squareSize / 4;\r
3320     }\r
3321     else {\r
3322         d_x += squareSize / 2;\r
3323     }\r
3324 \r
3325     s_x += squareSize / 2;\r
3326     s_y += squareSize / 2;\r
3327 \r
3328     /* Adjust width */\r
3329     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3330 \r
3331     /* Draw */\r
3332     stLB.lbStyle = BS_SOLID;\r
3333     stLB.lbColor = appData.highlightArrowColor;\r
3334     stLB.lbHatch = 0;\r
3335 \r
3336     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3337     holdpen = SelectObject( hdc, hpen );\r
3338     hbrush = CreateBrushIndirect( &stLB );\r
3339     holdbrush = SelectObject( hdc, hbrush );\r
3340 \r
3341     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3342 \r
3343     SelectObject( hdc, holdpen );\r
3344     SelectObject( hdc, holdbrush );\r
3345     DeleteObject( hpen );\r
3346     DeleteObject( hbrush );\r
3347 }\r
3348 \r
3349 BOOL HasHighlightInfo()\r
3350 {\r
3351     BOOL result = FALSE;\r
3352 \r
3353     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3354         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3355     {\r
3356         result = TRUE;\r
3357     }\r
3358 \r
3359     return result;\r
3360 \r
3361 \r
3362 \r
3363 }\r
3364 \r
3365 BOOL IsDrawArrowEnabled()\r
3366 {\r
3367     BOOL result = FALSE;\r
3368 \r
3369     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3370         result = TRUE;\r
3371     }\r
3372 \r
3373     return result;\r
3374 }\r
3375 \r
3376 VOID DrawArrowHighlight( HDC hdc )\r
3377 {\r
3378     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3379         DrawArrowBetweenSquares( hdc,\r
3380             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3381             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3382     }\r
3383 }\r
3384 \r
3385 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3386 {\r
3387     HRGN result = NULL;\r
3388 \r
3389     if( HasHighlightInfo() ) {\r
3390         int x1, y1, x2, y2;\r
3391         int sx, sy, dx, dy;\r
3392 \r
3393         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3394         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3395 \r
3396         sx = MIN( x1, x2 );\r
3397         sy = MIN( y1, y2 );\r
3398         dx = MAX( x1, x2 ) + squareSize;\r
3399         dy = MAX( y1, y2 ) + squareSize;\r
3400 \r
3401         result = CreateRectRgn( sx, sy, dx, dy );\r
3402     }\r
3403 \r
3404     return result;\r
3405 }\r
3406 \r
3407 /*\r
3408     Warning: this function modifies the behavior of several other functions. \r
3409     \r
3410     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3411     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3412     repaint is scattered all over the place, which is not good for features such as\r
3413     "arrow highlighting" that require a full repaint of the board.\r
3414 \r
3415     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3416     user interaction, when speed is not so important) but especially to avoid errors\r
3417     in the displayed graphics.\r
3418 \r
3419     In such patched places, I always try refer to this function so there is a single\r
3420     place to maintain knowledge.\r
3421     \r
3422     To restore the original behavior, just return FALSE unconditionally.\r
3423 */\r
3424 BOOL IsFullRepaintPreferrable()\r
3425 {\r
3426     BOOL result = FALSE;\r
3427 \r
3428     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3429         /* Arrow may appear on the board */\r
3430         result = TRUE;\r
3431     }\r
3432 \r
3433     return result;\r
3434 }\r
3435 \r
3436 /* \r
3437     This function is called by DrawPosition to know whether a full repaint must\r
3438     be forced or not.\r
3439 \r
3440     Only DrawPosition may directly call this function, which makes use of \r
3441     some state information. Other function should call DrawPosition specifying \r
3442     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3443 */\r
3444 BOOL DrawPositionNeedsFullRepaint()\r
3445 {\r
3446     BOOL result = FALSE;\r
3447 \r
3448     /* \r
3449         Probably a slightly better policy would be to trigger a full repaint\r
3450         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3451         but animation is fast enough that it's difficult to notice.\r
3452     */\r
3453     if( animInfo.piece == EmptySquare ) {\r
3454         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3455             result = TRUE;\r
3456         }\r
3457     }\r
3458 \r
3459     return result;\r
3460 }\r
3461 \r
3462 static HBITMAP borderBitmap;\r
3463 \r
3464 VOID\r
3465 DrawBackgroundOnDC(HDC hdc)\r
3466 {\r
3467   \r
3468   BITMAP bi;\r
3469   HDC tmphdc;\r
3470   HBITMAP hbm;\r
3471   static char oldBorder[MSG_SIZ];\r
3472   int w = 600, h = 600, mode;\r
3473 \r
3474   if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid\r
3475     strncpy(oldBorder, appData.border, MSG_SIZ-1);\r
3476     borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
3477   }\r
3478   if(borderBitmap == NULL) { // loading failed, use white\r
3479     FillRect( hdc, &boardRect, whitePieceBrush );\r
3480     return;\r
3481   }\r
3482   tmphdc = CreateCompatibleDC(hdc);\r
3483   hbm = SelectObject(tmphdc, borderBitmap);\r
3484   if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {\r
3485             w = bi.bmWidth;\r
3486             h = bi.bmHeight;\r
3487   }\r
3488   mode = SetStretchBltMode(hdc, COLORONCOLOR);\r
3489   StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left, \r
3490                   boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3491   SetStretchBltMode(hdc, mode);\r
3492   SelectObject(tmphdc, hbm);\r
3493   DeleteDC(tmphdc);\r
3494 }\r
3495 \r
3496 VOID\r
3497 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3498 {\r
3499   int row, column, x, y, square_color, piece_color;\r
3500   ChessSquare piece;\r
3501   HBRUSH oldBrush;\r
3502   HDC texture_hdc = NULL;\r
3503 \r
3504   /* [AS] Initialize background textures if needed */\r
3505   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3506       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3507       if( backTextureSquareSize != squareSize \r
3508        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3509           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3510           backTextureSquareSize = squareSize;\r
3511           RebuildTextureSquareInfo();\r
3512       }\r
3513 \r
3514       texture_hdc = CreateCompatibleDC( hdc );\r
3515   }\r
3516 \r
3517   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3518     for (column = 0; column < BOARD_WIDTH; column++) {\r
3519   \r
3520       SquareToPos(row, column, &x, &y);\r
3521 \r
3522       piece = board[row][column];\r
3523 \r
3524       square_color = ((column + row) % 2) == 1;\r
3525       if( gameInfo.variant == VariantXiangqi ) {\r
3526           square_color = !InPalace(row, column);\r
3527           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3528           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3529       }\r
3530       piece_color = (int) piece < (int) BlackPawn;\r
3531 \r
3532 \r
3533       /* [HGM] holdings file: light square or black */\r
3534       if(column == BOARD_LEFT-2) {\r
3535             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3536                 square_color = 1;\r
3537             else {\r
3538                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3539                 continue;\r
3540             }\r
3541       } else\r
3542       if(column == BOARD_RGHT + 1 ) {\r
3543             if( row < gameInfo.holdingsSize )\r
3544                 square_color = 1;\r
3545             else {\r
3546                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3547                 continue;\r
3548             }\r
3549       }\r
3550       if(column == BOARD_LEFT-1 ) /* left align */\r
3551             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3552       else if( column == BOARD_RGHT) /* right align */\r
3553             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3554       else if( piece == DarkSquare) DisplayHoldingsCount(hdc, x, y, 0, 0);\r
3555       else\r
3556       if (appData.monoMode) {\r
3557         if (piece == EmptySquare) {\r
3558           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3559                  square_color ? WHITENESS : BLACKNESS);\r
3560         } else {\r
3561           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3562         }\r
3563       } \r
3564       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3565           /* [AS] Draw the square using a texture bitmap */\r
3566           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3567           int r = row, c = column; // [HGM] do not flip board in flipView\r
3568           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3569 \r
3570           DrawTile( x, y, \r
3571               squareSize, squareSize, \r
3572               hdc, \r
3573               texture_hdc,\r
3574               backTextureSquareInfo[r][c].mode,\r
3575               backTextureSquareInfo[r][c].x,\r
3576               backTextureSquareInfo[r][c].y );\r
3577 \r
3578           SelectObject( texture_hdc, hbm );\r
3579 \r
3580           if (piece != EmptySquare) {\r
3581               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3582           }\r
3583       }\r
3584       else {\r
3585         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3586 \r
3587         oldBrush = SelectObject(hdc, brush );\r
3588         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3589         SelectObject(hdc, oldBrush);\r
3590         if (piece != EmptySquare)\r
3591           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3592       }\r
3593     }\r
3594   }\r
3595 \r
3596   if( texture_hdc != NULL ) {\r
3597     DeleteDC( texture_hdc );\r
3598   }\r
3599 }\r
3600 \r
3601 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3602 void fputDW(FILE *f, int x)\r
3603 {\r
3604         fputc(x     & 255, f);\r
3605         fputc(x>>8  & 255, f);\r
3606         fputc(x>>16 & 255, f);\r
3607         fputc(x>>24 & 255, f);\r
3608 }\r
3609 \r
3610 #define MAX_CLIPS 200   /* more than enough */\r
3611 \r
3612 VOID\r
3613 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3614 {\r
3615 //  HBITMAP bufferBitmap;\r
3616   BITMAP bi;\r
3617 //  RECT Rect;\r
3618   HDC tmphdc;\r
3619   HBITMAP hbm;\r
3620   int w = 100, h = 50;\r
3621 \r
3622   if(logo == NULL) {\r
3623     if(!logoHeight) return;\r
3624     FillRect( hdc, &logoRect, whitePieceBrush );\r
3625   }\r
3626 //  GetClientRect(hwndMain, &Rect);\r
3627 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3628 //                                      Rect.bottom-Rect.top+1);\r
3629   tmphdc = CreateCompatibleDC(hdc);\r
3630   hbm = SelectObject(tmphdc, logo);\r
3631   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3632             w = bi.bmWidth;\r
3633             h = bi.bmHeight;\r
3634   }\r
3635   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3636                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3637   SelectObject(tmphdc, hbm);\r
3638   DeleteDC(tmphdc);\r
3639 }\r
3640 \r
3641 VOID\r
3642 DisplayLogos()\r
3643 {\r
3644   if(logoHeight) {\r
3645         HDC hdc = GetDC(hwndMain);\r
3646         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3647         if(appData.autoLogo) {\r
3648           \r
3649           switch(gameMode) { // pick logos based on game mode\r
3650             case IcsObserving:\r
3651                 whiteLogo = second.programLogo; // ICS logo\r
3652                 blackLogo = second.programLogo;\r
3653             default:\r
3654                 break;\r
3655             case IcsPlayingWhite:\r
3656                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3657                 blackLogo = second.programLogo; // ICS logo\r
3658                 break;\r
3659             case IcsPlayingBlack:\r
3660                 whiteLogo = second.programLogo; // ICS logo\r
3661                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3662                 break;\r
3663             case TwoMachinesPlay:\r
3664                 if(first.twoMachinesColor[0] == 'b') {\r
3665                     whiteLogo = second.programLogo;\r
3666                     blackLogo = first.programLogo;\r
3667                 }\r
3668                 break;\r
3669             case MachinePlaysWhite:\r
3670                 blackLogo = userLogo;\r
3671                 break;\r
3672             case MachinePlaysBlack:\r
3673                 whiteLogo = userLogo;\r
3674                 blackLogo = first.programLogo;\r
3675           }\r
3676         }\r
3677         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3678         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3679         ReleaseDC(hwndMain, hdc);\r
3680   }\r
3681 }\r
3682 \r
3683 void\r
3684 UpdateLogos(int display)\r
3685 { // called after loading new engine(s), in tourney or from menu\r
3686   LoadLogo(&first, 0, FALSE);\r
3687   LoadLogo(&second, 1, appData.icsActive);\r
3688   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3689   if(display) DisplayLogos();\r
3690 }\r
3691 \r
3692 static HDC hdcSeek;\r
3693 \r
3694 // [HGM] seekgraph\r
3695 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3696 {\r
3697     POINT stPt;\r
3698     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3699     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3700     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3701     SelectObject( hdcSeek, hp );\r
3702 }\r
3703 \r
3704 // front-end wrapper for drawing functions to do rectangles\r
3705 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3706 {\r
3707     HPEN hp;\r
3708     RECT rc;\r
3709 \r
3710     if (hdcSeek == NULL) {\r
3711     hdcSeek = GetDC(hwndMain);\r
3712       if (!appData.monoMode) {\r
3713         SelectPalette(hdcSeek, hPal, FALSE);\r
3714         RealizePalette(hdcSeek);\r
3715       }\r
3716     }\r
3717     hp = SelectObject( hdcSeek, gridPen );\r
3718     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3719     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3720     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3721     SelectObject( hdcSeek, hp );\r
3722 }\r
3723 \r
3724 // front-end wrapper for putting text in graph\r
3725 void DrawSeekText(char *buf, int x, int y)\r
3726 {\r
3727         SIZE stSize;\r
3728         SetBkMode( hdcSeek, TRANSPARENT );\r
3729         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3730         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3731 }\r
3732 \r
3733 void DrawSeekDot(int x, int y, int color)\r
3734 {\r
3735         int square = color & 0x80;\r
3736         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3737                         color == 0 ? markerBrush[1] : color == 1 ? darkSquareBrush : explodeBrush);\r
3738         color &= 0x7F;\r
3739         if(square)\r
3740             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3741                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3742         else\r
3743             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3744                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3745             SelectObject(hdcSeek, oldBrush);\r
3746 }\r
3747 \r
3748 void DrawSeekOpen()\r
3749 {\r
3750 }\r
3751 \r
3752 void DrawSeekClose()\r
3753 {\r
3754 }\r
3755 \r
3756 VOID\r
3757 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3758 {\r
3759   static Board lastReq[2], lastDrawn[2];\r
3760   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3761   static int lastDrawnFlipView = 0;\r
3762   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3763   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3764   HDC tmphdc;\r
3765   HDC hdcmem;\r
3766   HBITMAP bufferBitmap;\r
3767   HBITMAP oldBitmap;\r
3768   RECT Rect;\r
3769   HRGN clips[MAX_CLIPS];\r
3770   ChessSquare dragged_piece = EmptySquare;\r
3771   int nr = twoBoards*partnerUp;\r
3772 \r
3773   /* I'm undecided on this - this function figures out whether a full\r
3774    * repaint is necessary on its own, so there's no real reason to have the\r
3775    * caller tell it that.  I think this can safely be set to FALSE - but\r
3776    * if we trust the callers not to request full repaints unnessesarily, then\r
3777    * we could skip some clipping work.  In other words, only request a full\r
3778    * redraw when the majority of pieces have changed positions (ie. flip, \r
3779    * gamestart and similar)  --Hawk\r
3780    */\r
3781   Boolean fullrepaint = repaint;\r
3782 \r
3783   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3784 \r
3785   if( DrawPositionNeedsFullRepaint() ) {\r
3786       fullrepaint = TRUE;\r
3787   }\r
3788 \r
3789   if (board == NULL) {\r
3790     if (!lastReqValid[nr]) {\r
3791       return;\r
3792     }\r
3793     board = lastReq[nr];\r
3794   } else {\r
3795     CopyBoard(lastReq[nr], board);\r
3796     lastReqValid[nr] = 1;\r
3797   }\r
3798 \r
3799   if (doingSizing) {\r
3800     return;\r
3801   }\r
3802 \r
3803   if (IsIconic(hwndMain)) {\r
3804     return;\r
3805   }\r
3806 \r
3807   if (hdc == NULL) {\r
3808     hdc = GetDC(hwndMain);\r
3809     if (!appData.monoMode) {\r
3810       SelectPalette(hdc, hPal, FALSE);\r
3811       RealizePalette(hdc);\r
3812     }\r
3813     releaseDC = TRUE;\r
3814   } else {\r
3815     releaseDC = FALSE;\r
3816   }\r
3817 \r
3818   /* Create some work-DCs */\r
3819   hdcmem = CreateCompatibleDC(hdc);\r
3820   tmphdc = CreateCompatibleDC(hdc);\r
3821 \r
3822   /* If dragging is in progress, we temporarely remove the piece */\r
3823   /* [HGM] or temporarily decrease count if stacked              */\r
3824   /*       !! Moved to before board compare !!                   */\r
3825   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3826     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3827     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3828             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3829         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3830     } else \r
3831     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3832             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3833         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3834     } else \r
3835         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3836   }\r
3837 \r
3838   /* Figure out which squares need updating by comparing the \r
3839    * newest board with the last drawn board and checking if\r
3840    * flipping has changed.\r
3841    */\r
3842   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3843     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3844       for (column = 0; column < BOARD_WIDTH; column++) {\r
3845         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3846           SquareToPos(row, column, &x, &y);\r
3847           clips[num_clips++] =\r
3848             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3849         }\r
3850       }\r
3851     }\r
3852    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3853     for (i=0; i<2; i++) {\r
3854       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3855           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3856         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3857             lastDrawnHighlight.sq[i].y >= 0) {\r
3858           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3859                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3860           clips[num_clips++] =\r
3861             CreateRectRgn(x - lineGap, y - lineGap, \r
3862                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3863         }\r
3864         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3865           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3866           clips[num_clips++] =\r
3867             CreateRectRgn(x - lineGap, y - lineGap, \r
3868                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3869         }\r
3870       }\r
3871     }\r
3872     for (i=0; i<2; i++) {\r
3873       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3874           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3875         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3876             lastDrawnPremove.sq[i].y >= 0) {\r
3877           SquareToPos(lastDrawnPremove.sq[i].y,\r
3878                       lastDrawnPremove.sq[i].x, &x, &y);\r
3879           clips[num_clips++] =\r
3880             CreateRectRgn(x - lineGap, y - lineGap, \r
3881                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3882         }\r
3883         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3884             premoveHighlightInfo.sq[i].y >= 0) {\r
3885           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3886                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3887           clips[num_clips++] =\r
3888             CreateRectRgn(x - lineGap, y - lineGap, \r
3889                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3890         }\r
3891       }\r
3892     }\r
3893    } else { // nr == 1\r
3894         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3895         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3896         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3897         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3898       for (i=0; i<2; i++) {\r
3899         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3900             partnerHighlightInfo.sq[i].y >= 0) {\r
3901           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3902                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3903           clips[num_clips++] =\r
3904             CreateRectRgn(x - lineGap, y - lineGap, \r
3905                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3906         }\r
3907         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3908             oldPartnerHighlight.sq[i].y >= 0) {\r
3909           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3910                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3911           clips[num_clips++] =\r
3912             CreateRectRgn(x - lineGap, y - lineGap, \r
3913                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3914         }\r
3915       }\r
3916    }\r
3917   } else {\r
3918     fullrepaint = TRUE;\r
3919   }\r
3920 \r
3921   /* Create a buffer bitmap - this is the actual bitmap\r
3922    * being written to.  When all the work is done, we can\r
3923    * copy it to the real DC (the screen).  This avoids\r
3924    * the problems with flickering.\r
3925    */\r
3926   GetClientRect(hwndMain, &Rect);\r
3927   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3928                                         Rect.bottom-Rect.top+1);\r
3929   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3930   if (!appData.monoMode) {\r
3931     SelectPalette(hdcmem, hPal, FALSE);\r
3932   }\r
3933 \r
3934   /* Create clips for dragging */\r
3935   if (!fullrepaint) {\r
3936     if (dragInfo.from.x >= 0) {\r
3937       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3938       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3939     }\r
3940     if (dragInfo.start.x >= 0) {\r
3941       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3942       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3943     }\r
3944     if (dragInfo.pos.x >= 0) {\r
3945       x = dragInfo.pos.x - squareSize / 2;\r
3946       y = dragInfo.pos.y - squareSize / 2;\r
3947       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3948     }\r
3949     if (dragInfo.lastpos.x >= 0) {\r
3950       x = dragInfo.lastpos.x - squareSize / 2;\r
3951       y = dragInfo.lastpos.y - squareSize / 2;\r
3952       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3953     }\r
3954   }\r
3955 \r
3956   /* Are we animating a move?  \r
3957    * If so, \r
3958    *   - remove the piece from the board (temporarely)\r
3959    *   - calculate the clipping region\r
3960    */\r
3961   if (!fullrepaint) {\r
3962     if (animInfo.piece != EmptySquare) {\r
3963       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3964       x = boardRect.left + animInfo.lastpos.x;\r
3965       y = boardRect.top + animInfo.lastpos.y;\r
3966       x2 = boardRect.left + animInfo.pos.x;\r
3967       y2 = boardRect.top + animInfo.pos.y;\r
3968       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3969       /* Slight kludge.  The real problem is that after AnimateMove is\r
3970          done, the position on the screen does not match lastDrawn.\r
3971          This currently causes trouble only on e.p. captures in\r
3972          atomic, where the piece moves to an empty square and then\r
3973          explodes.  The old and new positions both had an empty square\r
3974          at the destination, but animation has drawn a piece there and\r
3975          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3976       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3977     }\r
3978   }\r
3979 \r
3980   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3981   if (num_clips == 0)\r
3982     fullrepaint = TRUE;\r
3983 \r
3984   /* Set clipping on the memory DC */\r
3985   if (!fullrepaint) {\r
3986     SelectClipRgn(hdcmem, clips[0]);\r
3987     for (x = 1; x < num_clips; x++) {\r
3988       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3989         abort();  // this should never ever happen!\r
3990     }\r
3991   }\r
3992 \r
3993   /* Do all the drawing to the memory DC */\r
3994   if(explodeInfo.radius) { // [HGM] atomic\r
3995         HBRUSH oldBrush;\r
3996         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3997         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3998         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3999         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
4000         x += squareSize/2;\r
4001         y += squareSize/2;\r
4002         if(!fullrepaint) {\r
4003           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4004           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4005         }\r
4006         DrawGridOnDC(hdcmem);\r
4007         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
4008         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
4009         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4010         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
4011         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4012         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4013         SelectObject(hdcmem, oldBrush);\r
4014   } else {\r
4015     if(border) DrawBackgroundOnDC(hdcmem);\r
4016     DrawGridOnDC(hdcmem);\r
4017     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
4018         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
4019         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
4020     } else {\r
4021         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
4022         oldPartnerHighlight = partnerHighlightInfo;\r
4023     }\r
4024     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4025   }\r
4026   if(nr == 0) // [HGM] dual: markers only on left board\r
4027   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4028     for (column = 0; column < BOARD_WIDTH; column++) {\r
4029         if (marker[row][column]) { // marker changes only occur with full repaint!\r
4030             HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);\r
4031             SquareToPos(row, column, &x, &y);\r
4032             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
4033                           x + 3*squareSize/4, y + 3*squareSize/4);\r
4034             SelectObject(hdcmem, oldBrush);\r
4035         }\r
4036     }\r
4037   }\r
4038 \r
4039   if( appData.highlightMoveWithArrow ) {\r
4040 \r
4041     DrawArrowHighlight(hdcmem);\r
4042   }\r
4043 \r
4044   DrawCoordsOnDC(hdcmem);\r
4045 \r
4046   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
4047                  /* to make sure lastDrawn contains what is actually drawn */\r
4048 \r
4049   /* Put the dragged piece back into place and draw it (out of place!) */\r
4050     if (dragged_piece != EmptySquare) {\r
4051     /* [HGM] or restack */\r
4052     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4053                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4054     else\r
4055     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4056                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4057 \r
4058     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4059     x = dragInfo.pos.x - squareSize / 2;\r
4060     y = dragInfo.pos.y - squareSize / 2;\r
4061     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
4062                   ((int) dragInfo.piece < (int) BlackPawn), \r
4063                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4064   }   \r
4065   \r
4066   /* Put the animated piece back into place and draw it */\r
4067   if (animInfo.piece != EmptySquare) {\r
4068     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4069     x = boardRect.left + animInfo.pos.x;\r
4070     y = boardRect.top + animInfo.pos.y;\r
4071     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4072                   ((int) animInfo.piece < (int) BlackPawn),\r
4073                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4074   }\r
4075 \r
4076   /* Release the bufferBitmap by selecting in the old bitmap \r
4077    * and delete the memory DC\r
4078    */\r
4079   SelectObject(hdcmem, oldBitmap);\r
4080   DeleteDC(hdcmem);\r
4081 \r
4082   /* Set clipping on the target DC */\r
4083   if (!fullrepaint) {\r
4084     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
4085         RECT rect;\r
4086         GetRgnBox(clips[x], &rect);\r
4087         DeleteObject(clips[x]);\r
4088         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
4089                           rect.right + wpMain.width/2, rect.bottom);\r
4090     }\r
4091     SelectClipRgn(hdc, clips[0]);\r
4092     for (x = 1; x < num_clips; x++) {\r
4093       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4094         abort();   // this should never ever happen!\r
4095     } \r
4096   }\r
4097 \r
4098   /* Copy the new bitmap onto the screen in one go.\r
4099    * This way we avoid any flickering\r
4100    */\r
4101   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4102   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
4103          boardRect.right - boardRect.left,\r
4104          boardRect.bottom - boardRect.top,\r
4105          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4106   if(saveDiagFlag) { \r
4107     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
4108     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4109 \r
4110     GetObject(bufferBitmap, sizeof(b), &b);\r
4111     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
4112         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4113         bih.biWidth = b.bmWidth;\r
4114         bih.biHeight = b.bmHeight;\r
4115         bih.biPlanes = 1;\r
4116         bih.biBitCount = b.bmBitsPixel;\r
4117         bih.biCompression = 0;\r
4118         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4119         bih.biXPelsPerMeter = 0;\r
4120         bih.biYPelsPerMeter = 0;\r
4121         bih.biClrUsed = 0;\r
4122         bih.biClrImportant = 0;\r
4123 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4124 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4125         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4126 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4127 \r
4128         wb = b.bmWidthBytes;\r
4129         // count colors\r
4130         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4131                 int k = ((int*) pData)[i];\r
4132                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4133                 if(j >= 16) break;\r
4134                 color[j] = k;\r
4135                 if(j >= nrColors) nrColors = j+1;\r
4136         }\r
4137         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4138                 INT p = 0;\r
4139                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4140                     for(w=0; w<(wb>>2); w+=2) {\r
4141                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4142                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4143                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4144                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4145                         pData[p++] = m | j<<4;\r
4146                     }\r
4147                     while(p&3) pData[p++] = 0;\r
4148                 }\r
4149                 fac = 3;\r
4150                 wb = ((wb+31)>>5)<<2;\r
4151         }\r
4152         // write BITMAPFILEHEADER\r
4153         fprintf(diagFile, "BM");\r
4154         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4155         fputDW(diagFile, 0);\r
4156         fputDW(diagFile, 0x36 + (fac?64:0));\r
4157         // write BITMAPINFOHEADER\r
4158         fputDW(diagFile, 40);\r
4159         fputDW(diagFile, b.bmWidth);\r
4160         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4161         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4162         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4163         fputDW(diagFile, 0);\r
4164         fputDW(diagFile, 0);\r
4165         fputDW(diagFile, 0);\r
4166         fputDW(diagFile, 0);\r
4167         fputDW(diagFile, 0);\r
4168         fputDW(diagFile, 0);\r
4169         // write color table\r
4170         if(fac)\r
4171         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4172         // write bitmap data\r
4173         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4174                 fputc(pData[i], diagFile);\r
4175         free(pData);\r
4176      }\r
4177   }\r
4178 \r
4179   SelectObject(tmphdc, oldBitmap);\r
4180 \r
4181   /* Massive cleanup */\r
4182   for (x = 0; x < num_clips; x++)\r
4183     DeleteObject(clips[x]);\r
4184 \r
4185   DeleteDC(tmphdc);\r
4186   DeleteObject(bufferBitmap);\r
4187 \r
4188   if (releaseDC) \r
4189     ReleaseDC(hwndMain, hdc);\r
4190   \r
4191   if (lastDrawnFlipView != flipView && nr == 0) {\r
4192     if (flipView)\r
4193       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4194     else\r
4195       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4196   }\r
4197 \r
4198 /*  CopyBoard(lastDrawn, board);*/\r
4199   lastDrawnHighlight = highlightInfo;\r
4200   lastDrawnPremove   = premoveHighlightInfo;\r
4201   lastDrawnFlipView = flipView;\r
4202   lastDrawnValid[nr] = 1;\r
4203 }\r
4204 \r
4205 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4206 int\r
4207 SaveDiagram(f)\r
4208      FILE *f;\r
4209 {\r
4210     saveDiagFlag = 1; diagFile = f;\r
4211     HDCDrawPosition(NULL, TRUE, NULL);\r
4212     saveDiagFlag = 0;\r
4213 \r
4214     fclose(f);\r
4215     return TRUE;\r
4216 }\r
4217 \r
4218 \r
4219 /*---------------------------------------------------------------------------*\\r
4220 | CLIENT PAINT PROCEDURE\r
4221 |   This is the main event-handler for the WM_PAINT message.\r
4222 |\r
4223 \*---------------------------------------------------------------------------*/\r
4224 VOID\r
4225 PaintProc(HWND hwnd)\r
4226 {\r
4227   HDC         hdc;\r
4228   PAINTSTRUCT ps;\r
4229   HFONT       oldFont;\r
4230 \r
4231   if((hdc = BeginPaint(hwnd, &ps))) {\r
4232     if (IsIconic(hwnd)) {\r
4233       DrawIcon(hdc, 2, 2, iconCurrent);\r
4234     } else {\r
4235       if (!appData.monoMode) {\r
4236         SelectPalette(hdc, hPal, FALSE);\r
4237         RealizePalette(hdc);\r
4238       }\r
4239       HDCDrawPosition(hdc, 1, NULL);\r
4240       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4241         flipView = !flipView; partnerUp = !partnerUp;\r
4242         HDCDrawPosition(hdc, 1, NULL);\r
4243         flipView = !flipView; partnerUp = !partnerUp;\r
4244       }\r
4245       oldFont =\r
4246         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4247       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4248                  ETO_CLIPPED|ETO_OPAQUE,\r
4249                  &messageRect, messageText, strlen(messageText), NULL);\r
4250       SelectObject(hdc, oldFont);\r
4251       DisplayBothClocks();\r
4252       DisplayLogos();\r
4253     }\r
4254     EndPaint(hwnd,&ps);\r
4255   }\r
4256 \r
4257   return;\r
4258 }\r
4259 \r
4260 \r
4261 /*\r
4262  * If the user selects on a border boundary, return -1; if off the board,\r
4263  *   return -2.  Otherwise map the event coordinate to the square.\r
4264  * The offset boardRect.left or boardRect.top must already have been\r
4265  *   subtracted from x.\r
4266  */\r
4267 int EventToSquare(x, limit)\r
4268      int x, limit;\r
4269 {\r
4270   if (x <= border)\r
4271     return -2;\r
4272   if (x < lineGap + border)\r
4273     return -1;\r
4274   x -= lineGap + border;\r
4275   if ((x % (squareSize + lineGap)) >= squareSize)\r
4276     return -1;\r
4277   x /= (squareSize + lineGap);\r
4278     if (x >= limit)\r
4279     return -2;\r
4280   return x;\r
4281 }\r
4282 \r
4283 typedef struct {\r
4284   char piece;\r
4285   int command;\r
4286   char* name;\r
4287 } DropEnable;\r
4288 \r
4289 DropEnable dropEnables[] = {\r
4290   { 'P', DP_Pawn, N_("Pawn") },\r
4291   { 'N', DP_Knight, N_("Knight") },\r
4292   { 'B', DP_Bishop, N_("Bishop") },\r
4293   { 'R', DP_Rook, N_("Rook") },\r
4294   { 'Q', DP_Queen, N_("Queen") },\r
4295 };\r
4296 \r
4297 VOID\r
4298 SetupDropMenu(HMENU hmenu)\r
4299 {\r
4300   int i, count, enable;\r
4301   char *p;\r
4302   extern char white_holding[], black_holding[];\r
4303   char item[MSG_SIZ];\r
4304 \r
4305   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4306     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4307                dropEnables[i].piece);\r
4308     count = 0;\r
4309     while (p && *p++ == dropEnables[i].piece) count++;\r
4310       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4311     enable = count > 0 || !appData.testLegality\r
4312       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4313                       && !appData.icsActive);\r
4314     ModifyMenu(hmenu, dropEnables[i].command,\r
4315                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4316                dropEnables[i].command, item);\r
4317   }\r
4318 }\r
4319 \r
4320 void DragPieceBegin(int x, int y, Boolean instantly)\r
4321 {\r
4322       dragInfo.lastpos.x = boardRect.left + x;\r
4323       dragInfo.lastpos.y = boardRect.top + y;\r
4324       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4325       dragInfo.from.x = fromX;\r
4326       dragInfo.from.y = fromY;\r
4327       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4328       dragInfo.start = dragInfo.from;\r
4329       SetCapture(hwndMain);\r
4330 }\r
4331 \r
4332 void DragPieceEnd(int x, int y)\r
4333 {\r
4334     ReleaseCapture();\r
4335     dragInfo.start.x = dragInfo.start.y = -1;\r
4336     dragInfo.from = dragInfo.start;\r
4337     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4338 }\r
4339 \r
4340 void ChangeDragPiece(ChessSquare piece)\r
4341 {\r
4342     dragInfo.piece = piece;\r
4343 }\r
4344 \r
4345 /* Event handler for mouse messages */\r
4346 VOID\r
4347 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4348 {\r
4349   int x, y, menuNr;\r
4350   POINT pt;\r
4351   static int recursive = 0;\r
4352   HMENU hmenu;\r
4353   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4354 \r
4355   if (recursive) {\r
4356     if (message == WM_MBUTTONUP) {\r
4357       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4358          to the middle button: we simulate pressing the left button too!\r
4359          */\r
4360       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4361       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4362     }\r
4363     return;\r
4364   }\r
4365   recursive++;\r
4366   \r
4367   pt.x = LOWORD(lParam);\r
4368   pt.y = HIWORD(lParam);\r
4369   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4370   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4371   if (!flipView && y >= 0) {\r
4372     y = BOARD_HEIGHT - 1 - y;\r
4373   }\r
4374   if (flipView && x >= 0) {\r
4375     x = BOARD_WIDTH - 1 - x;\r
4376   }\r
4377 \r
4378   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4379   controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status\r
4380 \r
4381   switch (message) {\r
4382   case WM_LBUTTONDOWN:\r
4383       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4384         ClockClick(flipClock); break;\r
4385       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4386         ClockClick(!flipClock); break;\r
4387       }\r
4388     if(dragging) { // [HGM] lion: don't destroy dragging info if we are already dragging\r
4389       dragInfo.start.x = dragInfo.start.y = -1;\r
4390       dragInfo.from = dragInfo.start;\r
4391     }\r
4392     if(fromX == -1 && frozen) { // not sure where this is for\r
4393                 fromX = fromY = -1; \r
4394       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4395       break;\r
4396     }\r
4397       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4398       DrawPosition(TRUE, NULL);\r
4399     break;\r
4400 \r
4401   case WM_LBUTTONUP:\r
4402       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4403       DrawPosition(TRUE, NULL);\r
4404     break;\r
4405 \r
4406   case WM_MOUSEMOVE:\r
4407     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4408     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4409     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4410     if ((appData.animateDragging || appData.highlightDragging)\r
4411         && (wParam & MK_LBUTTON || dragging == 2)\r
4412         && dragInfo.from.x >= 0) \r
4413     {\r
4414       BOOL full_repaint = FALSE;\r
4415 \r
4416       if (appData.animateDragging) {\r
4417         dragInfo.pos = pt;\r
4418       }\r
4419       if (appData.highlightDragging) {\r
4420         HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);\r
4421         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4422             full_repaint = TRUE;\r
4423         }\r
4424       }\r
4425       \r
4426       DrawPosition( full_repaint, NULL);\r
4427       \r
4428       dragInfo.lastpos = dragInfo.pos;\r
4429     }\r
4430     break;\r
4431 \r
4432   case WM_MOUSEWHEEL: // [DM]\r
4433     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4434        /* Mouse Wheel is being rolled forward\r
4435         * Play moves forward\r
4436         */\r
4437        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4438                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4439        /* Mouse Wheel is being rolled backward\r
4440         * Play moves backward\r
4441         */\r
4442        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4443                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4444     }\r
4445     break;\r
4446 \r
4447   case WM_MBUTTONUP:\r
4448   case WM_RBUTTONUP:\r
4449     ReleaseCapture();\r
4450     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4451     break;\r
4452  \r
4453   case WM_MBUTTONDOWN:\r
4454   case WM_RBUTTONDOWN:\r
4455     ErrorPopDown();\r
4456     ReleaseCapture();\r
4457     fromX = fromY = -1;\r
4458     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4459     dragInfo.start.x = dragInfo.start.y = -1;\r
4460     dragInfo.from = dragInfo.start;\r
4461     dragInfo.lastpos = dragInfo.pos;\r
4462     if (appData.highlightDragging) {\r
4463       ClearHighlights();\r
4464     }\r
4465     if(y == -2) {\r
4466       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4467       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4468           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4469       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4470           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4471       }\r
4472       break;\r
4473     }\r
4474     DrawPosition(TRUE, NULL);\r
4475 \r
4476     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4477     switch (menuNr) {\r
4478     case 0:\r
4479       if (message == WM_MBUTTONDOWN) {\r
4480         buttonCount = 3;  /* even if system didn't think so */\r
4481         if (wParam & MK_SHIFT) \r
4482           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4483         else\r
4484           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4485       } else { /* message == WM_RBUTTONDOWN */\r
4486         /* Just have one menu, on the right button.  Windows users don't\r
4487            think to try the middle one, and sometimes other software steals\r
4488            it, or it doesn't really exist. */\r
4489         if(gameInfo.variant != VariantShogi)\r
4490             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4491         else\r
4492             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4493       }\r
4494       break;\r
4495     case 2:\r
4496       SetCapture(hwndMain);\r
4497       break;\r
4498     case 1:\r
4499       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4500       SetupDropMenu(hmenu);\r
4501       MenuPopup(hwnd, pt, hmenu, -1);\r
4502     default:\r
4503       break;\r
4504     }\r
4505     break;\r
4506   }\r
4507 \r
4508   recursive--;\r
4509 }\r
4510 \r
4511 /* Preprocess messages for buttons in main window */\r
4512 LRESULT CALLBACK\r
4513 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4514 {\r
4515   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4516   int i, dir;\r
4517 \r
4518   for (i=0; i<N_BUTTONS; i++) {\r
4519     if (buttonDesc[i].id == id) break;\r
4520   }\r
4521   if (i == N_BUTTONS) return 0;\r
4522   switch (message) {\r
4523   case WM_KEYDOWN:\r
4524     switch (wParam) {\r
4525     case VK_LEFT:\r
4526     case VK_RIGHT:\r
4527       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4528       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4529       return TRUE;\r
4530     }\r
4531     break;\r
4532   case WM_CHAR:\r
4533     switch (wParam) {\r
4534     case '\r':\r
4535       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4536       return TRUE;\r
4537     default:\r
4538       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4539         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4540         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4541         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4542         SetFocus(h);\r
4543         SendMessage(h, WM_CHAR, wParam, lParam);\r
4544         return TRUE;\r
4545       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4546         TypeInEvent((char)wParam);\r
4547       }\r
4548       break;\r
4549     }\r
4550     break;\r
4551   }\r
4552   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4553 }\r
4554 \r
4555 static int promoStyle;\r
4556 \r
4557 /* Process messages for Promotion dialog box */\r
4558 LRESULT CALLBACK\r
4559 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4560 {\r
4561 \r
4562   char promoChar;\r
4563 \r
4564   switch (message) {\r
4565 \r
4566   case WM_INITDIALOG: /* message: initialize dialog box */\r
4567     /* Center the dialog over the application window */\r
4568     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4569     Translate(hDlg, DLG_PromotionKing);\r
4570     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4571       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4572        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4573        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4574                SW_SHOW : SW_HIDE);\r
4575     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4576     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4577        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4578          PieceToChar(WhiteAngel) != '~') ||\r
4579         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4580          PieceToChar(BlackAngel) != '~')   ) ?\r
4581                SW_SHOW : SW_HIDE);\r
4582     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4583        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4584          PieceToChar(WhiteMarshall) != '~') ||\r
4585         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4586          PieceToChar(BlackMarshall) != '~')   ) ?\r
4587                SW_SHOW : SW_HIDE);\r
4588     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4589     ShowWindow(GetDlgItem(hDlg, PB_Rook),   !promoStyle ? SW_SHOW : SW_HIDE);\r
4590     ShowWindow(GetDlgItem(hDlg, PB_Bishop), !promoStyle ? SW_SHOW : SW_HIDE);\r
4591     if(promoStyle) {\r
4592         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4593         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4594         SetWindowText(hDlg, "Promote?");\r
4595     }\r
4596     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4597        gameInfo.variant == VariantSuper ?\r
4598                SW_SHOW : SW_HIDE);\r
4599     return TRUE;\r
4600 \r
4601   case WM_COMMAND: /* message: received a command */\r
4602     switch (LOWORD(wParam)) {\r
4603     case IDCANCEL:\r
4604       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4605       ClearHighlights();\r
4606       DrawPosition(FALSE, NULL);\r
4607       return TRUE;\r
4608     case PB_King:\r
4609       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4610       break;\r
4611     case PB_Queen:\r
4612       promoChar = promoStyle ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4613       break;\r
4614     case PB_Rook:\r
4615       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4616       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4617       break;\r
4618     case PB_Bishop:\r
4619       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4620       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4621       break;\r
4622     case PB_Chancellor:\r
4623       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4624       break;\r
4625     case PB_Archbishop:\r
4626       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4627       break;\r
4628     case PB_Knight:\r
4629       promoChar = gameInfo.variant == VariantShogi ? '=' : promoStyle ? NULLCHAR : \r
4630                   ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight));\r
4631       break;\r
4632     default:\r
4633       return FALSE;\r
4634     }\r
4635     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4636     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4637     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4638     fromX = fromY = -1;\r
4639     if (!appData.highlightLastMove) {\r
4640       ClearHighlights();\r
4641       DrawPosition(FALSE, NULL);\r
4642     }\r
4643     return TRUE;\r
4644   }\r
4645   return FALSE;\r
4646 }\r
4647 \r
4648 /* Pop up promotion dialog */\r
4649 VOID\r
4650 PromotionPopup(HWND hwnd)\r
4651 {\r
4652   FARPROC lpProc;\r
4653 \r
4654   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4655   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4656     hwnd, (DLGPROC)lpProc);\r
4657   FreeProcInstance(lpProc);\r
4658 }\r
4659 \r
4660 void\r
4661 PromotionPopUp(char choice)\r
4662 {\r
4663   promoStyle = (choice == '+' || IS_SHOGI(gameInfo.variant));\r
4664   DrawPosition(TRUE, NULL);\r
4665   PromotionPopup(hwndMain);\r
4666 }\r
4667 \r
4668 VOID\r
4669 LoadGameDialog(HWND hwnd, char* title)\r
4670 {\r
4671   UINT number = 0;\r
4672   FILE *f;\r
4673   char fileTitle[MSG_SIZ];\r
4674   f = OpenFileDialog(hwnd, "rb", "",\r
4675                      appData.oldSaveStyle ? "gam" : "pgn",\r
4676                      GAME_FILT,\r
4677                      title, &number, fileTitle, NULL);\r
4678   if (f != NULL) {\r
4679     cmailMsgLoaded = FALSE;\r
4680     if (number == 0) {\r
4681       int error = GameListBuild(f);\r
4682       if (error) {\r
4683         DisplayError(_("Cannot build game list"), error);\r
4684       } else if (!ListEmpty(&gameList) &&\r
4685                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4686         GameListPopUp(f, fileTitle);\r
4687         return;\r
4688       }\r
4689       GameListDestroy();\r
4690       number = 1;\r
4691     }\r
4692     LoadGame(f, number, fileTitle, FALSE);\r
4693   }\r
4694 }\r
4695 \r
4696 int get_term_width()\r
4697 {\r
4698     HDC hdc;\r
4699     TEXTMETRIC tm;\r
4700     RECT rc;\r
4701     HFONT hfont, hold_font;\r
4702     LOGFONT lf;\r
4703     HWND hText;\r
4704 \r
4705     if (hwndConsole)\r
4706         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4707     else\r
4708         return 79;\r
4709 \r
4710     // get the text metrics\r
4711     hdc = GetDC(hText);\r
4712     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4713     if (consoleCF.dwEffects & CFE_BOLD)\r
4714         lf.lfWeight = FW_BOLD;\r
4715     if (consoleCF.dwEffects & CFE_ITALIC)\r
4716         lf.lfItalic = TRUE;\r
4717     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4718         lf.lfStrikeOut = TRUE;\r
4719     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4720         lf.lfUnderline = TRUE;\r
4721     hfont = CreateFontIndirect(&lf);\r
4722     hold_font = SelectObject(hdc, hfont);\r
4723     GetTextMetrics(hdc, &tm);\r
4724     SelectObject(hdc, hold_font);\r
4725     DeleteObject(hfont);\r
4726     ReleaseDC(hText, hdc);\r
4727 \r
4728     // get the rectangle\r
4729     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4730 \r
4731     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4732 }\r
4733 \r
4734 void UpdateICSWidth(HWND hText)\r
4735 {\r
4736     LONG old_width, new_width;\r
4737 \r
4738     new_width = get_term_width(hText, FALSE);\r
4739     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4740     if (new_width != old_width)\r
4741     {\r
4742         ics_update_width(new_width);\r
4743         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4744     }\r
4745 }\r
4746 \r
4747 VOID\r
4748 ChangedConsoleFont()\r
4749 {\r
4750   CHARFORMAT cfmt;\r
4751   CHARRANGE tmpsel, sel;\r
4752   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4753   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4754   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4755   PARAFORMAT paraf;\r
4756 \r
4757   cfmt.cbSize = sizeof(CHARFORMAT);\r
4758   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4759     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4760                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4761   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4762    * size.  This was undocumented in the version of MSVC++ that I had\r
4763    * when I wrote the code, but is apparently documented now.\r
4764    */\r
4765   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4766   cfmt.bCharSet = f->lf.lfCharSet;\r
4767   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4768   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4769   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4770   /* Why are the following seemingly needed too? */\r
4771   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4772   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4773   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4774   tmpsel.cpMin = 0;\r
4775   tmpsel.cpMax = -1; /*999999?*/\r
4776   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4777   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4778   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4779    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4780    */\r
4781   paraf.cbSize = sizeof(paraf);\r
4782   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4783   paraf.dxStartIndent = 0;\r
4784   paraf.dxOffset = WRAP_INDENT;\r
4785   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4786   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4787   UpdateICSWidth(hText);\r
4788 }\r
4789 \r
4790 /*---------------------------------------------------------------------------*\\r
4791  *\r
4792  * Window Proc for main window\r
4793  *\r
4794 \*---------------------------------------------------------------------------*/\r
4795 \r
4796 /* Process messages for main window, etc. */\r
4797 LRESULT CALLBACK\r
4798 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4799 {\r
4800   FARPROC lpProc;\r
4801   int wmId;\r
4802   char *defName;\r
4803   FILE *f;\r
4804   UINT number;\r
4805   char fileTitle[MSG_SIZ];\r
4806   static SnapData sd;\r
4807   static int peek=0;\r
4808 \r
4809   switch (message) {\r
4810 \r
4811   case WM_PAINT: /* message: repaint portion of window */\r
4812     PaintProc(hwnd);\r
4813     break;\r
4814 \r
4815   case WM_ERASEBKGND:\r
4816     if (IsIconic(hwnd)) {\r
4817       /* Cheat; change the message */\r
4818       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4819     } else {\r
4820       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4821     }\r
4822     break;\r
4823 \r
4824   case WM_LBUTTONDOWN:\r
4825   case WM_MBUTTONDOWN:\r
4826   case WM_RBUTTONDOWN:\r
4827   case WM_LBUTTONUP:\r
4828   case WM_MBUTTONUP:\r
4829   case WM_RBUTTONUP:\r
4830   case WM_MOUSEMOVE:\r
4831   case WM_MOUSEWHEEL:\r
4832     MouseEvent(hwnd, message, wParam, lParam);\r
4833     break;\r
4834 \r
4835   case WM_KEYUP:\r
4836     if((char)wParam == '\b') {\r
4837       ForwardEvent(); peek = 0;\r
4838     }\r
4839 \r
4840     JAWS_KBUP_NAVIGATION\r
4841 \r
4842     break;\r
4843 \r
4844   case WM_KEYDOWN:\r
4845     if((char)wParam == '\b') {\r
4846       if(!peek) BackwardEvent(), peek = 1;\r
4847     }\r
4848 \r
4849     JAWS_KBDOWN_NAVIGATION\r
4850 \r
4851     break;\r
4852 \r
4853   case WM_CHAR:\r
4854     \r
4855     JAWS_ALT_INTERCEPT\r
4856 \r
4857     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4858         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4859         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4860         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4861         SetFocus(h);\r
4862         SendMessage(h, message, wParam, lParam);\r
4863     } else if(lParam != KF_REPEAT) {\r
4864         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4865                 TypeInEvent((char)wParam);\r
4866         } else if((char)wParam == 003) CopyGameToClipboard();\r
4867          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4868     }\r
4869 \r
4870     break;\r
4871 \r
4872   case WM_PALETTECHANGED:\r
4873     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4874       int nnew;\r
4875       HDC hdc = GetDC(hwndMain);\r
4876       SelectPalette(hdc, hPal, TRUE);\r
4877       nnew = RealizePalette(hdc);\r
4878       if (nnew > 0) {\r
4879         paletteChanged = TRUE;\r
4880 \r
4881         InvalidateRect(hwnd, &boardRect, FALSE);\r
4882       }\r
4883       ReleaseDC(hwnd, hdc);\r
4884     }\r
4885     break;\r
4886 \r
4887   case WM_QUERYNEWPALETTE:\r
4888     if (!appData.monoMode /*&& paletteChanged*/) {\r
4889       int nnew;\r
4890       HDC hdc = GetDC(hwndMain);\r
4891       paletteChanged = FALSE;\r
4892       SelectPalette(hdc, hPal, FALSE);\r
4893       nnew = RealizePalette(hdc);\r
4894       if (nnew > 0) {\r
4895         InvalidateRect(hwnd, &boardRect, FALSE);\r
4896       }\r
4897       ReleaseDC(hwnd, hdc);\r
4898       return TRUE;\r
4899     }\r
4900     return FALSE;\r
4901 \r
4902   case WM_COMMAND: /* message: command from application menu */\r
4903     wmId    = LOWORD(wParam);\r
4904 \r
4905     switch (wmId) {\r
4906     case IDM_NewGame:\r
4907       ResetGameEvent();\r
4908       SAY("new game enter a move to play against the computer with white");\r
4909       break;\r
4910 \r
4911     case IDM_NewGameFRC:\r
4912       if( NewGameFRC() == 0 ) {\r
4913         ResetGameEvent();\r
4914       }\r
4915       break;\r
4916 \r
4917     case IDM_NewVariant:\r
4918       NewVariantPopup(hwnd);\r
4919       break;\r
4920 \r
4921     case IDM_LoadGame:\r
4922       LoadGameDialog(hwnd, _("Load Game from File"));\r
4923       break;\r
4924 \r
4925     case IDM_LoadNextGame:\r
4926       ReloadGame(1);\r
4927       break;\r
4928 \r
4929     case IDM_LoadPrevGame:\r
4930       ReloadGame(-1);\r
4931       break;\r
4932 \r
4933     case IDM_ReloadGame:\r
4934       ReloadGame(0);\r
4935       break;\r
4936 \r
4937     case IDM_LoadPosition:\r
4938       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4939         Reset(FALSE, TRUE);\r
4940       }\r
4941       number = 1;\r
4942       f = OpenFileDialog(hwnd, "rb", "",\r
4943                          appData.oldSaveStyle ? "pos" : "fen",\r
4944                          POSITION_FILT,\r
4945                          _("Load Position from File"), &number, fileTitle, NULL);\r
4946       if (f != NULL) {\r
4947         LoadPosition(f, number, fileTitle);\r
4948       }\r
4949       break;\r
4950 \r
4951     case IDM_LoadNextPosition:\r
4952       ReloadPosition(1);\r
4953       break;\r
4954 \r
4955     case IDM_LoadPrevPosition:\r
4956       ReloadPosition(-1);\r
4957       break;\r
4958 \r
4959     case IDM_ReloadPosition:\r
4960       ReloadPosition(0);\r
4961       break;\r
4962 \r
4963     case IDM_SaveGame:\r
4964       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4965       f = OpenFileDialog(hwnd, "a", defName,\r
4966                          appData.oldSaveStyle ? "gam" : "pgn",\r
4967                          GAME_FILT,\r
4968                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4969       if (f != NULL) {\r
4970         SaveGame(f, 0, "");\r
4971       }\r
4972       break;\r
4973 \r
4974     case IDM_SavePosition:\r
4975       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4976       f = OpenFileDialog(hwnd, "a", defName,\r
4977                          appData.oldSaveStyle ? "pos" : "fen",\r
4978                          POSITION_FILT,\r
4979                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4980       if (f != NULL) {\r
4981         SavePosition(f, 0, "");\r
4982       }\r
4983       break;\r
4984 \r
4985     case IDM_SaveDiagram:\r
4986       defName = "diagram";\r
4987       f = OpenFileDialog(hwnd, "wb", defName,\r
4988                          "bmp",\r
4989                          DIAGRAM_FILT,\r
4990                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4991       if (f != NULL) {\r
4992         SaveDiagram(f);\r
4993       }\r
4994       break;\r
4995 \r
4996     case IDM_SaveSelected:\r
4997       f = OpenFileDialog(hwnd, "a", "",\r
4998                          "pgn",\r
4999                          GAME_FILT,\r
5000                          _("Save Game to File"), NULL, fileTitle, NULL);\r
5001       if (f != NULL) {\r
5002         SaveSelected(f, 0, "");\r
5003       }\r
5004       break;\r
5005 \r
5006     case IDM_CreateBook:\r
5007       CreateBookEvent();\r
5008       break;\r
5009 \r
5010     case IDM_CopyGame:\r
5011       CopyGameToClipboard();\r
5012       break;\r
5013 \r
5014     case IDM_PasteGame:\r
5015       PasteGameFromClipboard();\r
5016       break;\r
5017 \r
5018     case IDM_CopyGameListToClipboard:\r
5019       CopyGameListToClipboard();\r
5020       break;\r
5021 \r
5022     /* [AS] Autodetect FEN or PGN data */\r
5023     case IDM_PasteAny:\r
5024       PasteGameOrFENFromClipboard();\r
5025       break;\r
5026 \r
5027     /* [AS] Move history */\r
5028     case IDM_ShowMoveHistory:\r
5029         if( MoveHistoryIsUp() ) {\r
5030             MoveHistoryPopDown();\r
5031         }\r
5032         else {\r
5033             MoveHistoryPopUp();\r
5034         }\r
5035         break;\r
5036 \r
5037     /* [AS] Eval graph */\r
5038     case IDM_ShowEvalGraph:\r
5039         if( EvalGraphIsUp() ) {\r
5040             EvalGraphPopDown();\r
5041         }\r
5042         else {\r
5043             EvalGraphPopUp();\r
5044             SetFocus(hwndMain);\r
5045         }\r
5046         break;\r
5047 \r
5048     /* [AS] Engine output */\r
5049     case IDM_ShowEngineOutput:\r
5050         if( EngineOutputIsUp() ) {\r
5051             EngineOutputPopDown();\r
5052         }\r
5053         else {\r
5054             EngineOutputPopUp();\r
5055         }\r
5056         break;\r
5057 \r
5058     /* [AS] User adjudication */\r
5059     case IDM_UserAdjudication_White:\r
5060         UserAdjudicationEvent( +1 );\r
5061         break;\r
5062 \r
5063     case IDM_UserAdjudication_Black:\r
5064         UserAdjudicationEvent( -1 );\r
5065         break;\r
5066 \r
5067     case IDM_UserAdjudication_Draw:\r
5068         UserAdjudicationEvent( 0 );\r
5069         break;\r
5070 \r
5071     /* [AS] Game list options dialog */\r
5072     case IDM_GameListOptions:\r
5073       GameListOptions();\r
5074       break;\r
5075 \r
5076     case IDM_NewChat:\r
5077       ChatPopUp(NULL);\r
5078       break;\r
5079 \r
5080     case IDM_CopyPosition:\r
5081       CopyFENToClipboard();\r
5082       break;\r
5083 \r
5084     case IDM_PastePosition:\r
5085       PasteFENFromClipboard();\r
5086       break;\r
5087 \r
5088     case IDM_MailMove:\r
5089       MailMoveEvent();\r
5090       break;\r
5091 \r
5092     case IDM_ReloadCMailMsg:\r
5093       Reset(TRUE, TRUE);\r
5094       ReloadCmailMsgEvent(FALSE);\r
5095       break;\r
5096 \r
5097     case IDM_Minimize:\r
5098       ShowWindow(hwnd, SW_MINIMIZE);\r
5099       break;\r
5100 \r
5101     case IDM_Exit:\r
5102       ExitEvent(0);\r
5103       break;\r
5104 \r
5105     case IDM_MachineWhite:\r
5106       MachineWhiteEvent();\r
5107       /*\r
5108        * refresh the tags dialog only if it's visible\r
5109        */\r
5110       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5111           char *tags;\r
5112           tags = PGNTags(&gameInfo);\r
5113           TagsPopUp(tags, CmailMsg());\r
5114           free(tags);\r
5115       }\r
5116       SAY("computer starts playing white");\r
5117       break;\r
5118 \r
5119     case IDM_MachineBlack:\r
5120       MachineBlackEvent();\r
5121       /*\r
5122        * refresh the tags dialog only if it's visible\r
5123        */\r
5124       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5125           char *tags;\r
5126           tags = PGNTags(&gameInfo);\r
5127           TagsPopUp(tags, CmailMsg());\r
5128           free(tags);\r
5129       }\r
5130       SAY("computer starts playing black");\r
5131       break;\r
5132 \r
5133     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
5134       if(matchMode) EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_GRAYED);\r
5135       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
5136       break;\r
5137 \r
5138     case IDM_TwoMachines:\r
5139       TwoMachinesEvent();\r
5140       /*\r
5141 \r
5142        * refresh the tags dialog only if it's visible\r
5143        */\r
5144       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5145           char *tags;\r
5146           tags = PGNTags(&gameInfo);\r
5147           TagsPopUp(tags, CmailMsg());\r
5148           free(tags);\r
5149       }\r
5150       SAY("computer starts playing both sides");\r
5151       break;\r
5152 \r
5153     case IDM_AnalysisMode:\r
5154       if(AnalyzeModeEvent()) {\r
5155         SAY("analyzing current position");\r
5156       }\r
5157       break;\r
5158 \r
5159     case IDM_AnalyzeFile:\r
5160       AnalyzeFileEvent();\r
5161       break;\r
5162 \r
5163     case IDM_IcsClient:\r
5164       IcsClientEvent();\r
5165       break;\r
5166 \r
5167     case IDM_EditGame:\r
5168     case IDM_EditGame2:\r
5169       EditGameEvent();\r
5170       SAY("edit game");\r
5171       break;\r
5172 \r
5173     case IDM_EditPosition:\r
5174     case IDM_EditPosition2:\r
5175       EditPositionEvent();\r
5176       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
5177       break;\r
5178 \r
5179     case IDM_Training:\r
5180       TrainingEvent();\r
5181       break;\r
5182 \r
5183     case IDM_ShowGameList:\r
5184       ShowGameListProc();\r
5185       break;\r
5186 \r
5187     case IDM_EditProgs1:\r
5188       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
5189       break;\r
5190 \r
5191     case IDM_LoadProg1:\r
5192      LoadEnginePopUp(hwndMain, 0);\r
5193       break;\r
5194 \r
5195     case IDM_LoadProg2:\r
5196      LoadEnginePopUp(hwndMain, 1);\r
5197       break;\r
5198 \r
5199     case IDM_EditServers:\r
5200       EditTagsPopUp(icsNames, &icsNames);\r
5201       break;\r
5202 \r
5203     case IDM_EditTags:\r
5204     case IDM_Tags:\r
5205       EditTagsProc();\r
5206       break;\r
5207 \r
5208     case IDM_EditBook:\r
5209       EditBookEvent();\r
5210       break;\r
5211 \r
5212     case IDM_EditComment:\r
5213     case IDM_Comment:\r
5214       if (commentUp && editComment) {\r
5215         CommentPopDown();\r
5216       } else {\r
5217         EditCommentEvent();\r
5218       }\r
5219       break;\r
5220 \r
5221     case IDM_Pause:\r
5222       PauseEvent();\r
5223       break;\r
5224 \r
5225     case IDM_Accept:\r
5226       AcceptEvent();\r
5227       break;\r
5228 \r
5229     case IDM_Decline:\r
5230       DeclineEvent();\r
5231       break;\r
5232 \r
5233     case IDM_Rematch:\r
5234 \r
5235       RematchEvent();\r
5236       break;\r
5237 \r
5238     case IDM_CallFlag:\r
5239       CallFlagEvent();\r
5240       break;\r
5241 \r
5242     case IDM_Draw:\r
5243       DrawEvent();\r
5244       break;\r
5245 \r
5246     case IDM_Adjourn:\r
5247       AdjournEvent();\r
5248       break;\r
5249 \r
5250     case IDM_Abort:\r
5251       AbortEvent();\r
5252       break;\r
5253 \r
5254     case IDM_Resign:\r
5255       ResignEvent();\r
5256       break;\r
5257 \r
5258     case IDM_StopObserving:\r
5259       StopObservingEvent();\r
5260       break;\r
5261 \r
5262     case IDM_StopExamining:\r
5263       StopExaminingEvent();\r
5264       break;\r
5265 \r
5266     case IDM_Upload:\r
5267       UploadGameEvent();\r
5268       break;\r
5269 \r
5270     case IDM_TypeInMove:\r
5271       TypeInEvent('\000');\r
5272       break;\r
5273 \r
5274     case IDM_TypeInName:\r
5275       PopUpNameDialog('\000');\r
5276       break;\r
5277 \r
5278     case IDM_Backward:\r
5279       BackwardEvent();\r
5280       SetFocus(hwndMain);\r
5281       break;\r
5282 \r
5283     JAWS_MENU_ITEMS\r
5284 \r
5285     case IDM_Forward:\r
5286       ForwardEvent();\r
5287       SetFocus(hwndMain);\r
5288       break;\r
5289 \r
5290     case IDM_ToStart:\r
5291       ToStartEvent();\r
5292       SetFocus(hwndMain);\r
5293       break;\r
5294 \r
5295     case IDM_ToEnd:\r
5296       ToEndEvent();\r
5297       SetFocus(hwndMain);\r
5298       break;\r
5299 \r
5300     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5301     case OPT_GameListPrev:\r
5302       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5303       break;\r
5304 \r
5305     case IDM_Revert:\r
5306       RevertEvent(FALSE);\r
5307       break;\r
5308 \r
5309     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5310       RevertEvent(TRUE);\r
5311       break;\r
5312 \r
5313     case IDM_TruncateGame:\r
5314       TruncateGameEvent();\r
5315       break;\r
5316 \r
5317     case IDM_MoveNow:\r
5318       MoveNowEvent();\r
5319       break;\r
5320 \r
5321     case IDM_RetractMove:\r
5322       RetractMoveEvent();\r
5323       break;\r
5324 \r
5325     case IDM_FlipView:\r
5326       flipView = !flipView;\r
5327       DrawPosition(FALSE, NULL);\r
5328       break;\r
5329 \r
5330     case IDM_FlipClock:\r
5331       flipClock = !flipClock;\r
5332       DisplayBothClocks();\r
5333       DisplayLogos();\r
5334       break;\r
5335 \r
5336     case IDM_MuteSounds:\r
5337       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5338       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5339                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5340       break;\r
5341 \r
5342     case IDM_GeneralOptions:\r
5343       GeneralOptionsPopup(hwnd);\r
5344       DrawPosition(TRUE, NULL);\r
5345       break;\r
5346 \r
5347     case IDM_BoardOptions:\r
5348       BoardOptionsPopup(hwnd);\r
5349       break;\r
5350 \r
5351     case IDM_ThemeOptions:\r
5352       ThemeOptionsPopup(hwnd);\r
5353       break;\r
5354 \r
5355     case IDM_EnginePlayOptions:\r
5356       EnginePlayOptionsPopup(hwnd);\r
5357       break;\r
5358 \r
5359     case IDM_Engine1Options:\r
5360       EngineOptionsPopup(hwnd, &first);\r
5361       break;\r
5362 \r
5363     case IDM_Engine2Options:\r
5364       savedHwnd = hwnd;\r
5365       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5366       EngineOptionsPopup(hwnd, &second);\r
5367       break;\r
5368 \r
5369     case IDM_OptionsUCI:\r
5370       UciOptionsPopup(hwnd);\r
5371       break;\r
5372 \r
5373     case IDM_Tourney:\r
5374       TourneyPopup(hwnd);\r
5375       break;\r
5376 \r
5377     case IDM_IcsOptions:\r
5378       IcsOptionsPopup(hwnd);\r
5379       break;\r
5380 \r
5381     case IDM_Fonts:\r
5382       FontsOptionsPopup(hwnd);\r
5383       break;\r
5384 \r
5385     case IDM_Sounds:\r
5386       SoundOptionsPopup(hwnd);\r
5387       break;\r
5388 \r
5389     case IDM_CommPort:\r
5390       CommPortOptionsPopup(hwnd);\r
5391       break;\r
5392 \r
5393     case IDM_LoadOptions:\r
5394       LoadOptionsPopup(hwnd);\r
5395       break;\r
5396 \r
5397     case IDM_SaveOptions:\r
5398       SaveOptionsPopup(hwnd);\r
5399       break;\r
5400 \r
5401     case IDM_TimeControl:\r
5402       TimeControlOptionsPopup(hwnd);\r
5403       break;\r
5404 \r
5405     case IDM_SaveSettings:\r
5406       SaveSettings(settingsFileName);\r
5407       break;\r
5408 \r
5409     case IDM_SaveSettingsOnExit:\r
5410       saveSettingsOnExit = !saveSettingsOnExit;\r
5411       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5412                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5413                                          MF_CHECKED : MF_UNCHECKED));\r
5414       break;\r
5415 \r
5416     case IDM_Hint:\r
5417       HintEvent();\r
5418       break;\r
5419 \r
5420     case IDM_Book:\r
5421       BookEvent();\r
5422       break;\r
5423 \r
5424     case IDM_AboutGame:\r
5425       AboutGameEvent();\r
5426       break;\r
5427 \r
5428     case IDM_Debug:\r
5429       appData.debugMode = !appData.debugMode;\r
5430       if (appData.debugMode) {\r
5431         char dir[MSG_SIZ];\r
5432         GetCurrentDirectory(MSG_SIZ, dir);\r
5433         SetCurrentDirectory(installDir);\r
5434         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5435         SetCurrentDirectory(dir);\r
5436         setbuf(debugFP, NULL);\r
5437       } else {\r
5438         fclose(debugFP);\r
5439         debugFP = NULL;\r
5440       }\r
5441       break;\r
5442 \r
5443     case IDM_HELPCONTENTS:\r
5444       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5445           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5446           MessageBox (GetFocus(),\r
5447                     _("Unable to activate help"),\r
5448                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5449       }\r
5450       break;\r
5451 \r
5452     case IDM_HELPSEARCH:\r
5453         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5454             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5455         MessageBox (GetFocus(),\r
5456                     _("Unable to activate help"),\r
5457                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5458       }\r
5459       break;\r
5460 \r
5461     case IDM_HELPHELP:\r
5462       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5463         MessageBox (GetFocus(),\r
5464                     _("Unable to activate help"),\r
5465                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5466       }\r
5467       break;\r
5468 \r
5469     case IDM_ABOUT:\r
5470       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5471       DialogBox(hInst, \r
5472         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5473         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5474       FreeProcInstance(lpProc);\r
5475       break;\r
5476 \r
5477     case IDM_DirectCommand1:\r
5478       AskQuestionEvent(_("Direct Command"),\r
5479                        _("Send to chess program:"), "", "1");\r
5480       break;\r
5481     case IDM_DirectCommand2:\r
5482       AskQuestionEvent(_("Direct Command"),\r
5483                        _("Send to second chess program:"), "", "2");\r
5484       break;\r
5485 \r
5486     case EP_WhitePawn:\r
5487       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5488       fromX = fromY = -1;\r
5489       break;\r
5490 \r
5491     case EP_WhiteKnight:\r
5492       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5493       fromX = fromY = -1;\r
5494       break;\r
5495 \r
5496     case EP_WhiteBishop:\r
5497       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5498       fromX = fromY = -1;\r
5499       break;\r
5500 \r
5501     case EP_WhiteRook:\r
5502       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5503       fromX = fromY = -1;\r
5504       break;\r
5505 \r
5506     case EP_WhiteQueen:\r
5507       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5508       fromX = fromY = -1;\r
5509       break;\r
5510 \r
5511     case EP_WhiteFerz:\r
5512       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5513       fromX = fromY = -1;\r
5514       break;\r
5515 \r
5516     case EP_WhiteWazir:\r
5517       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5518       fromX = fromY = -1;\r
5519       break;\r
5520 \r
5521     case EP_WhiteAlfil:\r
5522       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5523       fromX = fromY = -1;\r
5524       break;\r
5525 \r
5526     case EP_WhiteCannon:\r
5527       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5528       fromX = fromY = -1;\r
5529       break;\r
5530 \r
5531     case EP_WhiteCardinal:\r
5532       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5533       fromX = fromY = -1;\r
5534       break;\r
5535 \r
5536     case EP_WhiteMarshall:\r
5537       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5538       fromX = fromY = -1;\r
5539       break;\r
5540 \r
5541     case EP_WhiteKing:\r
5542       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5543       fromX = fromY = -1;\r
5544       break;\r
5545 \r
5546     case EP_BlackPawn:\r
5547       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5548       fromX = fromY = -1;\r
5549       break;\r
5550 \r
5551     case EP_BlackKnight:\r
5552       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5553       fromX = fromY = -1;\r
5554       break;\r
5555 \r
5556     case EP_BlackBishop:\r
5557       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5558       fromX = fromY = -1;\r
5559       break;\r
5560 \r
5561     case EP_BlackRook:\r
5562       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5563       fromX = fromY = -1;\r
5564       break;\r
5565 \r
5566     case EP_BlackQueen:\r
5567       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5568       fromX = fromY = -1;\r
5569       break;\r
5570 \r
5571     case EP_BlackFerz:\r
5572       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5573       fromX = fromY = -1;\r
5574       break;\r
5575 \r
5576     case EP_BlackWazir:\r
5577       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5578       fromX = fromY = -1;\r
5579       break;\r
5580 \r
5581     case EP_BlackAlfil:\r
5582       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5583       fromX = fromY = -1;\r
5584       break;\r
5585 \r
5586     case EP_BlackCannon:\r
5587       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5588       fromX = fromY = -1;\r
5589       break;\r
5590 \r
5591     case EP_BlackCardinal:\r
5592       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5593       fromX = fromY = -1;\r
5594       break;\r
5595 \r
5596     case EP_BlackMarshall:\r
5597       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5598       fromX = fromY = -1;\r
5599       break;\r
5600 \r
5601     case EP_BlackKing:\r
5602       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5603       fromX = fromY = -1;\r
5604       break;\r
5605 \r
5606     case EP_EmptySquare:\r
5607       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5608       fromX = fromY = -1;\r
5609       break;\r
5610 \r
5611     case EP_ClearBoard:\r
5612       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5613       fromX = fromY = -1;\r
5614       break;\r
5615 \r
5616     case EP_White:\r
5617       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5618       fromX = fromY = -1;\r
5619       break;\r
5620 \r
5621     case EP_Black:\r
5622       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5623       fromX = fromY = -1;\r
5624       break;\r
5625 \r
5626     case EP_Promote:\r
5627       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5628       fromX = fromY = -1;\r
5629       break;\r
5630 \r
5631     case EP_Demote:\r
5632       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5633       fromX = fromY = -1;\r
5634       break;\r
5635 \r
5636     case DP_Pawn:\r
5637       DropMenuEvent(WhitePawn, fromX, fromY);\r
5638       fromX = fromY = -1;\r
5639       break;\r
5640 \r
5641     case DP_Knight:\r
5642       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5643       fromX = fromY = -1;\r
5644       break;\r
5645 \r
5646     case DP_Bishop:\r
5647       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5648       fromX = fromY = -1;\r
5649       break;\r
5650 \r
5651     case DP_Rook:\r
5652       DropMenuEvent(WhiteRook, fromX, fromY);\r
5653       fromX = fromY = -1;\r
5654       break;\r
5655 \r
5656     case DP_Queen:\r
5657       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5658       fromX = fromY = -1;\r
5659       break;\r
5660 \r
5661     case IDM_English:\r
5662       barbaric = 0; appData.language = "";\r
5663       TranslateMenus(0);\r
5664       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5665       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5666       lastChecked = wmId;\r
5667       break;\r
5668 \r
5669     default:\r
5670       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5671           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5672       else\r
5673       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5674           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5675           TranslateMenus(0);\r
5676           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5677           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5678           lastChecked = wmId;\r
5679           break;\r
5680       }\r
5681       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5682     }\r
5683     break;\r
5684 \r
5685   case WM_TIMER:\r
5686     switch (wParam) {\r
5687     case CLOCK_TIMER_ID:\r
5688       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5689       clockTimerEvent = 0;\r
5690       DecrementClocks(); /* call into back end */\r
5691       break;\r
5692     case LOAD_GAME_TIMER_ID:\r
5693       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5694       loadGameTimerEvent = 0;\r
5695       AutoPlayGameLoop(); /* call into back end */\r
5696       break;\r
5697     case ANALYSIS_TIMER_ID:\r
5698       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5699                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5700         AnalysisPeriodicEvent(0);\r
5701       } else {\r
5702         KillTimer(hwnd, analysisTimerEvent);\r
5703         analysisTimerEvent = 0;\r
5704       }\r
5705       break;\r
5706     case DELAYED_TIMER_ID:\r
5707       KillTimer(hwnd, delayedTimerEvent);\r
5708       delayedTimerEvent = 0;\r
5709       delayedTimerCallback();\r
5710       break;\r
5711     }\r
5712     break;\r
5713 \r
5714   case WM_USER_Input:\r
5715     InputEvent(hwnd, message, wParam, lParam);\r
5716     break;\r
5717 \r
5718   /* [AS] Also move "attached" child windows */\r
5719   case WM_WINDOWPOSCHANGING:\r
5720 \r
5721     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5722         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5723 \r
5724         if( ((lpwp->flags & SWP_NOMOVE) == 0) /*&& ((lpwp->flags & SWP_NOSIZE) != 0)*/ ) { // [HGM] in Win8 size always accompanies move?\r
5725             /* Window is moving */\r
5726             RECT rcMain;\r
5727 \r
5728 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5729             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5730             rcMain.right  = wpMain.x + wpMain.width;\r
5731             rcMain.top    = wpMain.y;\r
5732             rcMain.bottom = wpMain.y + wpMain.height;\r
5733             \r
5734             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5735             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5736             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5737             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5738             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5739             wpMain.x = lpwp->x;\r
5740             wpMain.y = lpwp->y;\r
5741 \r
5742         }\r
5743     }\r
5744     break;\r
5745 \r
5746   /* [AS] Snapping */\r
5747   case WM_ENTERSIZEMOVE:\r
5748     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5749     if (hwnd == hwndMain) {\r
5750       doingSizing = TRUE;\r
5751       lastSizing = 0;\r
5752     }\r
5753     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5754     break;\r
5755 \r
5756   case WM_SIZING:\r
5757     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5758     if (hwnd == hwndMain) {\r
5759       lastSizing = wParam;\r
5760     }\r
5761     break;\r
5762 \r
5763   case WM_MOVING:\r
5764     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5765       return OnMoving( &sd, hwnd, wParam, lParam );\r
5766 \r
5767   case WM_EXITSIZEMOVE:\r
5768     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5769     if (hwnd == hwndMain) {\r
5770       RECT client;\r
5771       doingSizing = FALSE;\r
5772       InvalidateRect(hwnd, &boardRect, FALSE);\r
5773       GetClientRect(hwnd, &client);\r
5774       ResizeBoard(client.right, client.bottom, lastSizing);\r
5775       lastSizing = 0;\r
5776       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5777     }\r
5778     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5779     break;\r
5780 \r
5781   case WM_DESTROY: /* message: window being destroyed */\r
5782     PostQuitMessage(0);\r
5783     break;\r
5784 \r
5785   case WM_CLOSE:\r
5786     if (hwnd == hwndMain) {\r
5787       ExitEvent(0);\r
5788     }\r
5789     break;\r
5790 \r
5791   default:      /* Passes it on if unprocessed */\r
5792     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5793   }\r
5794 \r
5795 \r
5796   return 0;\r
5797 }\r
5798 \r
5799 /*---------------------------------------------------------------------------*\\r
5800  *\r
5801  * Misc utility routines\r
5802  *\r
5803 \*---------------------------------------------------------------------------*/\r
5804 \r
5805 /*\r
5806  * Decent random number generator, at least not as bad as Windows\r
5807  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5808  */\r
5809 unsigned int randstate;\r
5810 \r
5811 int\r
5812 myrandom(void)\r
5813 {\r
5814   randstate = randstate * 1664525 + 1013904223;\r
5815   return (int) randstate & 0x7fffffff;\r
5816 }\r
5817 \r
5818 void\r
5819 mysrandom(unsigned int seed)\r
5820 {\r
5821   randstate = seed;\r
5822 }\r
5823 \r
5824 \r
5825 /* \r
5826  * returns TRUE if user selects a different color, FALSE otherwise \r
5827  */\r
5828 \r
5829 BOOL\r
5830 ChangeColor(HWND hwnd, COLORREF *which)\r
5831 {\r
5832   static BOOL firstTime = TRUE;\r
5833   static DWORD customColors[16];\r
5834   CHOOSECOLOR cc;\r
5835   COLORREF newcolor;\r
5836   int i;\r
5837   ColorClass ccl;\r
5838 \r
5839   if (firstTime) {\r
5840     /* Make initial colors in use available as custom colors */\r
5841     /* Should we put the compiled-in defaults here instead? */\r
5842     i = 0;\r
5843     customColors[i++] = lightSquareColor & 0xffffff;\r
5844     customColors[i++] = darkSquareColor & 0xffffff;\r
5845     customColors[i++] = whitePieceColor & 0xffffff;\r
5846     customColors[i++] = blackPieceColor & 0xffffff;\r
5847     customColors[i++] = highlightSquareColor & 0xffffff;\r
5848     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5849 \r
5850     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5851       customColors[i++] = textAttribs[ccl].color;\r
5852     }\r
5853     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5854     firstTime = FALSE;\r
5855   }\r
5856 \r
5857   cc.lStructSize = sizeof(cc);\r
5858   cc.hwndOwner = hwnd;\r
5859   cc.hInstance = NULL;\r
5860   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5861   cc.lpCustColors = (LPDWORD) customColors;\r
5862   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5863 \r
5864   if (!ChooseColor(&cc)) return FALSE;\r
5865 \r
5866   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5867   if (newcolor == *which) return FALSE;\r
5868   *which = newcolor;\r
5869   return TRUE;\r
5870 \r
5871   /*\r
5872   InitDrawingColors();\r
5873   InvalidateRect(hwnd, &boardRect, FALSE);\r
5874   */\r
5875 }\r
5876 \r
5877 BOOLEAN\r
5878 MyLoadSound(MySound *ms)\r
5879 {\r
5880   BOOL ok = FALSE;\r
5881   struct stat st;\r
5882   FILE *f;\r
5883 \r
5884   if (ms->data && ms->flag) free(ms->data);\r
5885   ms->data = NULL;\r
5886 \r
5887   switch (ms->name[0]) {\r
5888   case NULLCHAR:\r
5889     /* Silence */\r
5890     ok = TRUE;\r
5891     break;\r
5892   case '$':\r
5893     /* System sound from Control Panel.  Don't preload here. */\r
5894     ok = TRUE;\r
5895     break;\r
5896   case '!':\r
5897     if (ms->name[1] == NULLCHAR) {\r
5898       /* "!" alone = silence */\r
5899       ok = TRUE;\r
5900     } else {\r
5901       /* Builtin wave resource.  Error if not found. */\r
5902       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5903       if (h == NULL) break;\r
5904       ms->data = (void *)LoadResource(hInst, h);\r
5905       ms->flag = 0; // not maloced, so cannot be freed!\r
5906       if (h == NULL) break;\r
5907       ok = TRUE;\r
5908     }\r
5909     break;\r
5910   default:\r
5911     /* .wav file.  Error if not found. */\r
5912     f = fopen(ms->name, "rb");\r
5913     if (f == NULL) break;\r
5914     if (fstat(fileno(f), &st) < 0) break;\r
5915     ms->data = malloc(st.st_size);\r
5916     ms->flag = 1;\r
5917     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5918     fclose(f);\r
5919     ok = TRUE;\r
5920     break;\r
5921   }\r
5922   if (!ok) {\r
5923     char buf[MSG_SIZ];\r
5924       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5925     DisplayError(buf, GetLastError());\r
5926   }\r
5927   return ok;\r
5928 }\r
5929 \r
5930 BOOLEAN\r
5931 MyPlaySound(MySound *ms)\r
5932 {\r
5933   BOOLEAN ok = FALSE;\r
5934 \r
5935   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5936   switch (ms->name[0]) {\r
5937   case NULLCHAR:\r
5938         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5939     /* Silence */\r
5940     ok = TRUE;\r
5941     break;\r
5942   case '$':\r
5943     /* System sound from Control Panel (deprecated feature).\r
5944        "$" alone or an unset sound name gets default beep (still in use). */\r
5945     if (ms->name[1]) {\r
5946       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5947     }\r
5948     if (!ok) ok = MessageBeep(MB_OK);\r
5949     break; \r
5950   case '!':\r
5951     /* Builtin wave resource, or "!" alone for silence */\r
5952     if (ms->name[1]) {\r
5953       if (ms->data == NULL) return FALSE;\r
5954       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5955     } else {\r
5956       ok = TRUE;\r
5957     }\r
5958     break;\r
5959   default:\r
5960     /* .wav file.  Error if not found. */\r
5961     if (ms->data == NULL) return FALSE;\r
5962     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5963     break;\r
5964   }\r
5965   /* Don't print an error: this can happen innocently if the sound driver\r
5966      is busy; for instance, if another instance of WinBoard is playing\r
5967      a sound at about the same time. */\r
5968   return ok;\r
5969 }\r
5970 \r
5971 \r
5972 LRESULT CALLBACK\r
5973 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5974 {\r
5975   BOOL ok;\r
5976   OPENFILENAME *ofn;\r
5977   static UINT *number; /* gross that this is static */\r
5978 \r
5979   switch (message) {\r
5980   case WM_INITDIALOG: /* message: initialize dialog box */\r
5981     /* Center the dialog over the application window */\r
5982     ofn = (OPENFILENAME *) lParam;\r
5983     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5984       number = (UINT *) ofn->lCustData;\r
5985       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5986     } else {\r
5987       number = NULL;\r
5988     }\r
5989     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5990     Translate(hDlg, 1536);\r
5991     return FALSE;  /* Allow for further processing */\r
5992 \r
5993   case WM_COMMAND:\r
5994     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5995       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5996     }\r
5997     return FALSE;  /* Allow for further processing */\r
5998   }\r
5999   return FALSE;\r
6000 }\r
6001 \r
6002 UINT APIENTRY\r
6003 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6004 {\r
6005   static UINT *number;\r
6006   OPENFILENAME *ofname;\r
6007   OFNOTIFY *ofnot;\r
6008   switch (uiMsg) {\r
6009   case WM_INITDIALOG:\r
6010     Translate(hdlg, DLG_IndexNumber);\r
6011     ofname = (OPENFILENAME *)lParam;\r
6012     number = (UINT *)(ofname->lCustData);\r
6013     break;\r
6014   case WM_NOTIFY:\r
6015     ofnot = (OFNOTIFY *)lParam;\r
6016     if (ofnot->hdr.code == CDN_FILEOK) {\r
6017       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6018     }\r
6019     break;\r
6020   }\r
6021   return 0;\r
6022 }\r
6023 \r
6024 \r
6025 FILE *\r
6026 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6027                char *nameFilt, char *dlgTitle, UINT *number,\r
6028                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6029 {\r
6030   OPENFILENAME openFileName;\r
6031   char buf1[MSG_SIZ];\r
6032   FILE *f;\r
6033 \r
6034   if (fileName == NULL) fileName = buf1;\r
6035   if (defName == NULL) {\r
6036     safeStrCpy(fileName, "*.", 3 );\r
6037     strcat(fileName, defExt);\r
6038   } else {\r
6039     safeStrCpy(fileName, defName, MSG_SIZ );\r
6040   }\r
6041     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
6042   if (number) *number = 0;\r
6043 \r
6044   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6045   openFileName.hwndOwner         = hwnd;\r
6046   openFileName.hInstance         = (HANDLE) hInst;\r
6047   openFileName.lpstrFilter       = nameFilt;\r
6048   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6049   openFileName.nMaxCustFilter    = 0L;\r
6050   openFileName.nFilterIndex      = 1L;\r
6051   openFileName.lpstrFile         = fileName;\r
6052   openFileName.nMaxFile          = MSG_SIZ;\r
6053   openFileName.lpstrFileTitle    = fileTitle;\r
6054   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6055   openFileName.lpstrInitialDir   = NULL;\r
6056   openFileName.lpstrTitle        = dlgTitle;\r
6057   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6058     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6059     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6060     | (oldDialog ? 0 : OFN_EXPLORER);\r
6061   openFileName.nFileOffset       = 0;\r
6062   openFileName.nFileExtension    = 0;\r
6063   openFileName.lpstrDefExt       = defExt;\r
6064   openFileName.lCustData         = (LONG) number;\r
6065   openFileName.lpfnHook          = oldDialog ?\r
6066     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6067   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6068 \r
6069   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6070                         GetOpenFileName(&openFileName)) {\r
6071     /* open the file */\r
6072     f = fopen(openFileName.lpstrFile, write);\r
6073     if (f == NULL) {\r
6074       MessageBox(hwnd, _("File open failed"), NULL,\r
6075                  MB_OK|MB_ICONEXCLAMATION);\r
6076       return NULL;\r
6077     }\r
6078   } else {\r
6079     int err = CommDlgExtendedError();\r
6080     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
6081     return FALSE;\r
6082   }\r
6083   return f;\r
6084 }\r
6085 \r
6086 \r
6087 \r
6088 VOID APIENTRY\r
6089 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6090 {\r
6091   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6092 \r
6093   /*\r
6094    * Get the first pop-up menu in the menu template. This is the\r
6095    * menu that TrackPopupMenu displays.\r
6096    */\r
6097   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6098   TranslateOneMenu(10, hmenuTrackPopup);\r
6099 \r
6100   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6101 \r
6102   /*\r
6103    * TrackPopup uses screen coordinates, so convert the\r
6104    * coordinates of the mouse click to screen coordinates.\r
6105    */\r
6106   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6107 \r
6108   /* Draw and track the floating pop-up menu. */\r
6109   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6110                  pt.x, pt.y, 0, hwnd, NULL);\r
6111 \r
6112   /* Destroy the menu.*/\r
6113   DestroyMenu(hmenu);\r
6114 }\r
6115    \r
6116 typedef struct {\r
6117   HWND hDlg, hText;\r
6118   int sizeX, sizeY, newSizeX, newSizeY;\r
6119   HDWP hdwp;\r
6120 } ResizeEditPlusButtonsClosure;\r
6121 \r
6122 BOOL CALLBACK\r
6123 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6124 {\r
6125   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6126   RECT rect;\r
6127   POINT pt;\r
6128 \r
6129   if (hChild == cl->hText) return TRUE;\r
6130   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6131   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6132   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6133   ScreenToClient(cl->hDlg, &pt);\r
6134   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6135     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6136   return TRUE;\r
6137 }\r
6138 \r
6139 /* Resize a dialog that has a (rich) edit field filling most of\r
6140    the top, with a row of buttons below */\r
6141 VOID\r
6142 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6143 {\r
6144   RECT rectText;\r
6145   int newTextHeight, newTextWidth;\r
6146   ResizeEditPlusButtonsClosure cl;\r
6147   \r
6148   /*if (IsIconic(hDlg)) return;*/\r
6149   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6150   \r
6151   cl.hdwp = BeginDeferWindowPos(8);\r
6152 \r
6153   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6154   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6155   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6156   if (newTextHeight < 0) {\r
6157     newSizeY += -newTextHeight;\r
6158     newTextHeight = 0;\r
6159   }\r
6160   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6161     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6162 \r
6163   cl.hDlg = hDlg;\r
6164   cl.hText = hText;\r
6165   cl.sizeX = sizeX;\r
6166   cl.sizeY = sizeY;\r
6167   cl.newSizeX = newSizeX;\r
6168   cl.newSizeY = newSizeY;\r
6169   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6170 \r
6171   EndDeferWindowPos(cl.hdwp);\r
6172 }\r
6173 \r
6174 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6175 {\r
6176     RECT    rChild, rParent;\r
6177     int     wChild, hChild, wParent, hParent;\r
6178     int     wScreen, hScreen, xNew, yNew;\r
6179     HDC     hdc;\r
6180 \r
6181     /* Get the Height and Width of the child window */\r
6182     GetWindowRect (hwndChild, &rChild);\r
6183     wChild = rChild.right - rChild.left;\r
6184     hChild = rChild.bottom - rChild.top;\r
6185 \r
6186     /* Get the Height and Width of the parent window */\r
6187     GetWindowRect (hwndParent, &rParent);\r
6188     wParent = rParent.right - rParent.left;\r
6189     hParent = rParent.bottom - rParent.top;\r
6190 \r
6191     /* Get the display limits */\r
6192     hdc = GetDC (hwndChild);\r
6193     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6194     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6195     ReleaseDC(hwndChild, hdc);\r
6196 \r
6197     /* Calculate new X position, then adjust for screen */\r
6198     xNew = rParent.left + ((wParent - wChild) /2);\r
6199     if (xNew < 0) {\r
6200         xNew = 0;\r
6201     } else if ((xNew+wChild) > wScreen) {\r
6202         xNew = wScreen - wChild;\r
6203     }\r
6204 \r
6205     /* Calculate new Y position, then adjust for screen */\r
6206     if( mode == 0 ) {\r
6207         yNew = rParent.top  + ((hParent - hChild) /2);\r
6208     }\r
6209     else {\r
6210         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6211     }\r
6212 \r
6213     if (yNew < 0) {\r
6214         yNew = 0;\r
6215     } else if ((yNew+hChild) > hScreen) {\r
6216         yNew = hScreen - hChild;\r
6217     }\r
6218 \r
6219     /* Set it, and return */\r
6220     return SetWindowPos (hwndChild, NULL,\r
6221                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6222 }\r
6223 \r
6224 /* Center one window over another */\r
6225 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6226 {\r
6227     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6228 }\r
6229 \r
6230 /*---------------------------------------------------------------------------*\\r
6231  *\r
6232  * Startup Dialog functions\r
6233  *\r
6234 \*---------------------------------------------------------------------------*/\r
6235 void\r
6236 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6237 {\r
6238   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6239 \r
6240   while (*cd != NULL) {\r
6241     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6242     cd++;\r
6243   }\r
6244 }\r
6245 \r
6246 void\r
6247 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6248 {\r
6249   char buf1[MAX_ARG_LEN];\r
6250   int len;\r
6251 \r
6252   if (str[0] == '@') {\r
6253     FILE* f = fopen(str + 1, "r");\r
6254     if (f == NULL) {\r
6255       DisplayFatalError(str + 1, errno, 2);\r
6256       return;\r
6257     }\r
6258     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6259     fclose(f);\r
6260     buf1[len] = NULLCHAR;\r
6261     str = buf1;\r
6262   }\r
6263 \r
6264   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6265 \r
6266   for (;;) {\r
6267     char buf[MSG_SIZ];\r
6268     char *end = strchr(str, '\n');\r
6269     if (end == NULL) return;\r
6270     memcpy(buf, str, end - str);\r
6271     buf[end - str] = NULLCHAR;\r
6272     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6273     str = end + 1;\r
6274   }\r
6275 }\r
6276 \r
6277 void\r
6278 SetStartupDialogEnables(HWND hDlg)\r
6279 {\r
6280   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6281     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6282     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6283   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6284     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6285   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6286     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6287   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6288     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6289   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6290     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6291     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6292     IsDlgButtonChecked(hDlg, OPT_View));\r
6293 }\r
6294 \r
6295 char *\r
6296 QuoteForFilename(char *filename)\r
6297 {\r
6298   int dquote, space;\r
6299   dquote = strchr(filename, '"') != NULL;\r
6300   space = strchr(filename, ' ') != NULL;\r
6301   if (dquote || space) {\r
6302     if (dquote) {\r
6303       return "'";\r
6304     } else {\r
6305       return "\"";\r
6306     }\r
6307   } else {\r
6308     return "";\r
6309   }\r
6310 }\r
6311 \r
6312 VOID\r
6313 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6314 {\r
6315   char buf[MSG_SIZ];\r
6316   char *q;\r
6317 \r
6318   InitComboStringsFromOption(hwndCombo, nthnames);\r
6319   q = QuoteForFilename(nthcp);\r
6320     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6321   if (*nthdir != NULLCHAR) {\r
6322     q = QuoteForFilename(nthdir);\r
6323       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6324   }\r
6325   if (*nthcp == NULLCHAR) {\r
6326     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6327   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6328     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6329     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6330   }\r
6331 }\r
6332 \r
6333 LRESULT CALLBACK\r
6334 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6335 {\r
6336   char buf[MSG_SIZ];\r
6337   HANDLE hwndCombo;\r
6338   char *p;\r
6339 \r
6340   switch (message) {\r
6341   case WM_INITDIALOG:\r
6342     /* Center the dialog */\r
6343     CenterWindow (hDlg, GetDesktopWindow());\r
6344     Translate(hDlg, DLG_Startup);\r
6345     /* Initialize the dialog items */\r
6346     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6347                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6348                   firstChessProgramNames);\r
6349     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6350                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6351                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6352     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6353     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6354       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6355     if (*appData.icsHelper != NULLCHAR) {\r
6356       char *q = QuoteForFilename(appData.icsHelper);\r
6357       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6358     }\r
6359     if (*appData.icsHost == NULLCHAR) {\r
6360       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6361       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6362     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6363       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6364       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6365     }\r
6366 \r
6367     if (appData.icsActive) {\r
6368       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6369     }\r
6370     else if (appData.noChessProgram) {\r
6371       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6372     }\r
6373     else {\r
6374       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6375     }\r
6376 \r
6377     SetStartupDialogEnables(hDlg);\r
6378     return TRUE;\r
6379 \r
6380   case WM_COMMAND:\r
6381     switch (LOWORD(wParam)) {\r
6382     case IDOK:\r
6383       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6384         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6385         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6386         p = buf;\r
6387         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6388         ParseArgs(StringGet, &p);\r
6389         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6390         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6391         p = buf;\r
6392         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6393         ParseArgs(StringGet, &p);\r
6394         SwapEngines(singleList); // ... and then make it 'second'\r
6395 \r
6396         appData.noChessProgram = FALSE;\r
6397         appData.icsActive = FALSE;\r
6398       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6399         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6400         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6401         p = buf;\r
6402         ParseArgs(StringGet, &p);\r
6403         if (appData.zippyPlay) {\r
6404           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6405           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6406           p = buf;\r
6407           ParseArgs(StringGet, &p);\r
6408         }\r
6409       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6410         appData.noChessProgram = TRUE;\r
6411         appData.icsActive = FALSE;\r
6412       } else {\r
6413         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6414                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6415         return TRUE;\r
6416       }\r
6417       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6418         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6419         p = buf;\r
6420         ParseArgs(StringGet, &p);\r
6421       }\r
6422       EndDialog(hDlg, TRUE);\r
6423       return TRUE;\r
6424 \r
6425     case IDCANCEL:\r
6426       ExitEvent(0);\r
6427       return TRUE;\r
6428 \r
6429     case IDM_HELPCONTENTS:\r
6430       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6431         MessageBox (GetFocus(),\r
6432                     _("Unable to activate help"),\r
6433                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6434       }\r
6435       break;\r
6436 \r
6437     default:\r
6438       SetStartupDialogEnables(hDlg);\r
6439       break;\r
6440     }\r
6441     break;\r
6442   }\r
6443   return FALSE;\r
6444 }\r
6445 \r
6446 /*---------------------------------------------------------------------------*\\r
6447  *\r
6448  * About box dialog functions\r
6449  *\r
6450 \*---------------------------------------------------------------------------*/\r
6451 \r
6452 /* Process messages for "About" dialog box */\r
6453 LRESULT CALLBACK\r
6454 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6455 {\r
6456   switch (message) {\r
6457   case WM_INITDIALOG: /* message: initialize dialog box */\r
6458     /* Center the dialog over the application window */\r
6459     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6460     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6461     Translate(hDlg, ABOUTBOX);\r
6462     JAWS_COPYRIGHT\r
6463     return (TRUE);\r
6464 \r
6465   case WM_COMMAND: /* message: received a command */\r
6466     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6467         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6468       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6469       return (TRUE);\r
6470     }\r
6471     break;\r
6472   }\r
6473   return (FALSE);\r
6474 }\r
6475 \r
6476 /*---------------------------------------------------------------------------*\\r
6477  *\r
6478  * Comment Dialog functions\r
6479  *\r
6480 \*---------------------------------------------------------------------------*/\r
6481 \r
6482 LRESULT CALLBACK\r
6483 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6484 {\r
6485   static HANDLE hwndText = NULL;\r
6486   int len, newSizeX, newSizeY;\r
6487   static int sizeX, sizeY;\r
6488   char *str;\r
6489   RECT rect;\r
6490   MINMAXINFO *mmi;\r
6491 \r
6492   switch (message) {\r
6493   case WM_INITDIALOG: /* message: initialize dialog box */\r
6494     /* Initialize the dialog items */\r
6495     Translate(hDlg, DLG_EditComment);\r
6496     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6497     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6498     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6499     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6500     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6501     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6502     SetWindowText(hDlg, commentTitle);\r
6503     if (editComment) {\r
6504       SetFocus(hwndText);\r
6505     } else {\r
6506       SetFocus(GetDlgItem(hDlg, IDOK));\r
6507     }\r
6508     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6509                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6510                 MAKELPARAM(FALSE, 0));\r
6511     /* Size and position the dialog */\r
6512     if (!commentDialog) {\r
6513       commentDialog = hDlg;\r
6514       GetClientRect(hDlg, &rect);\r
6515       sizeX = rect.right;\r
6516       sizeY = rect.bottom;\r
6517       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6518           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6519         WINDOWPLACEMENT wp;\r
6520         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6521         wp.length = sizeof(WINDOWPLACEMENT);\r
6522         wp.flags = 0;\r
6523         wp.showCmd = SW_SHOW;\r
6524         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6525         wp.rcNormalPosition.left = wpComment.x;\r
6526         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6527         wp.rcNormalPosition.top = wpComment.y;\r
6528         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6529         SetWindowPlacement(hDlg, &wp);\r
6530 \r
6531         GetClientRect(hDlg, &rect);\r
6532         newSizeX = rect.right;\r
6533         newSizeY = rect.bottom;\r
6534         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6535                               newSizeX, newSizeY);\r
6536         sizeX = newSizeX;\r
6537         sizeY = newSizeY;\r
6538       }\r
6539     }\r
6540     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6541     return FALSE;\r
6542 \r
6543   case WM_COMMAND: /* message: received a command */\r
6544     switch (LOWORD(wParam)) {\r
6545     case IDOK:\r
6546       if (editComment) {\r
6547         char *p, *q;\r
6548         /* Read changed options from the dialog box */\r
6549         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6550         len = GetWindowTextLength(hwndText);\r
6551         str = (char *) malloc(len + 1);\r
6552         GetWindowText(hwndText, str, len + 1);\r
6553         p = q = str;\r
6554         while (*q) {\r
6555           if (*q == '\r')\r
6556             q++;\r
6557           else\r
6558             *p++ = *q++;\r
6559         }\r
6560         *p = NULLCHAR;\r
6561         ReplaceComment(commentIndex, str);\r
6562         free(str);\r
6563       }\r
6564       CommentPopDown();\r
6565       return TRUE;\r
6566 \r
6567     case IDCANCEL:\r
6568     case OPT_CancelComment:\r
6569       CommentPopDown();\r
6570       return TRUE;\r
6571 \r
6572     case OPT_ClearComment:\r
6573       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6574       break;\r
6575 \r
6576     case OPT_EditComment:\r
6577       EditCommentEvent();\r
6578       return TRUE;\r
6579 \r
6580     default:\r
6581       break;\r
6582     }\r
6583     break;\r
6584 \r
6585   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6586         if( wParam == OPT_CommentText ) {\r
6587             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6588 \r
6589             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6590                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6591                 POINTL pt;\r
6592                 LRESULT index;\r
6593 \r
6594                 pt.x = LOWORD( lpMF->lParam );\r
6595                 pt.y = HIWORD( lpMF->lParam );\r
6596 \r
6597                 if(lpMF->msg == WM_CHAR) {\r
6598                         CHARRANGE sel;\r
6599                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6600                         index = sel.cpMin;\r
6601                 } else\r
6602                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6603 \r
6604                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6605                 len = GetWindowTextLength(hwndText);\r
6606                 str = (char *) malloc(len + 1);\r
6607                 GetWindowText(hwndText, str, len + 1);\r
6608                 ReplaceComment(commentIndex, str);\r
6609                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6610                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6611                 free(str);\r
6612 \r
6613                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6614                 lpMF->msg = WM_USER;\r
6615 \r
6616                 return TRUE;\r
6617             }\r
6618         }\r
6619         break;\r
6620 \r
6621   case WM_SIZE:\r
6622     newSizeX = LOWORD(lParam);\r
6623     newSizeY = HIWORD(lParam);\r
6624     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6625     sizeX = newSizeX;\r
6626     sizeY = newSizeY;\r
6627     break;\r
6628 \r
6629   case WM_GETMINMAXINFO:\r
6630     /* Prevent resizing window too small */\r
6631     mmi = (MINMAXINFO *) lParam;\r
6632     mmi->ptMinTrackSize.x = 100;\r
6633     mmi->ptMinTrackSize.y = 100;\r
6634     break;\r
6635   }\r
6636   return FALSE;\r
6637 }\r
6638 \r
6639 VOID\r
6640 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6641 {\r
6642   FARPROC lpProc;\r
6643   char *p, *q;\r
6644 \r
6645   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6646 \r
6647   if (str == NULL) str = "";\r
6648   p = (char *) malloc(2 * strlen(str) + 2);\r
6649   q = p;\r
6650   while (*str) {\r
6651     if (*str == '\n') *q++ = '\r';\r
6652     *q++ = *str++;\r
6653   }\r
6654   *q = NULLCHAR;\r
6655   if (commentText != NULL) free(commentText);\r
6656 \r
6657   commentIndex = index;\r
6658   commentTitle = title;\r
6659   commentText = p;\r
6660   editComment = edit;\r
6661 \r
6662   if (commentDialog) {\r
6663     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6664     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6665   } else {\r
6666     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6667     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6668                  hwndMain, (DLGPROC)lpProc);\r
6669     FreeProcInstance(lpProc);\r
6670   }\r
6671   commentUp = TRUE;\r
6672 }\r
6673 \r
6674 \r
6675 /*---------------------------------------------------------------------------*\\r
6676  *\r
6677  * Type-in move dialog functions\r
6678  * \r
6679 \*---------------------------------------------------------------------------*/\r
6680 \r
6681 LRESULT CALLBACK\r
6682 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6683 {\r
6684   char move[MSG_SIZ];\r
6685   HWND hInput;\r
6686 \r
6687   switch (message) {\r
6688   case WM_INITDIALOG:\r
6689     move[0] = (char) lParam;\r
6690     move[1] = NULLCHAR;\r
6691     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6692     Translate(hDlg, DLG_TypeInMove);\r
6693     hInput = GetDlgItem(hDlg, OPT_Move);\r
6694     SetWindowText(hInput, move);\r
6695     SetFocus(hInput);\r
6696     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6697     return FALSE;\r
6698 \r
6699   case WM_COMMAND:\r
6700     switch (LOWORD(wParam)) {\r
6701     case IDOK:\r
6702 \r
6703       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6704       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6705       TypeInDoneEvent(move);\r
6706       EndDialog(hDlg, TRUE);\r
6707       return TRUE;\r
6708     case IDCANCEL:\r
6709       EndDialog(hDlg, FALSE);\r
6710       return TRUE;\r
6711     default:\r
6712       break;\r
6713     }\r
6714     break;\r
6715   }\r
6716   return FALSE;\r
6717 }\r
6718 \r
6719 VOID\r
6720 PopUpMoveDialog(char firstchar)\r
6721 {\r
6722     FARPROC lpProc;\r
6723 \r
6724       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6725       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6726         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6727       FreeProcInstance(lpProc);\r
6728 }\r
6729 \r
6730 /*---------------------------------------------------------------------------*\\r
6731  *\r
6732  * Type-in name dialog functions\r
6733  * \r
6734 \*---------------------------------------------------------------------------*/\r
6735 \r
6736 LRESULT CALLBACK\r
6737 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6738 {\r
6739   char move[MSG_SIZ];\r
6740   HWND hInput;\r
6741 \r
6742   switch (message) {\r
6743   case WM_INITDIALOG:\r
6744     move[0] = (char) lParam;\r
6745     move[1] = NULLCHAR;\r
6746     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6747     Translate(hDlg, DLG_TypeInName);\r
6748     hInput = GetDlgItem(hDlg, OPT_Name);\r
6749     SetWindowText(hInput, move);\r
6750     SetFocus(hInput);\r
6751     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6752     return FALSE;\r
6753 \r
6754   case WM_COMMAND:\r
6755     switch (LOWORD(wParam)) {\r
6756     case IDOK:\r
6757       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6758       appData.userName = strdup(move);\r
6759       SetUserLogo(); DisplayLogos();\r
6760       SetGameInfo();\r
6761       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6762         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6763         DisplayTitle(move);\r
6764       }\r
6765 \r
6766 \r
6767       EndDialog(hDlg, TRUE);\r
6768       return TRUE;\r
6769     case IDCANCEL:\r
6770       EndDialog(hDlg, FALSE);\r
6771       return TRUE;\r
6772     default:\r
6773       break;\r
6774     }\r
6775     break;\r
6776   }\r
6777   return FALSE;\r
6778 }\r
6779 \r
6780 VOID\r
6781 PopUpNameDialog(char firstchar)\r
6782 {\r
6783     FARPROC lpProc;\r
6784     \r
6785       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6786       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6787         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6788       FreeProcInstance(lpProc);\r
6789 }\r
6790 \r
6791 /*---------------------------------------------------------------------------*\\r
6792  *\r
6793  *  Error dialogs\r
6794  * \r
6795 \*---------------------------------------------------------------------------*/\r
6796 \r
6797 /* Nonmodal error box */\r
6798 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6799                              WPARAM wParam, LPARAM lParam);\r
6800 \r
6801 VOID\r
6802 ErrorPopUp(char *title, char *content)\r
6803 {\r
6804   FARPROC lpProc;\r
6805   char *p, *q;\r
6806   BOOLEAN modal = hwndMain == NULL;\r
6807 \r
6808   p = content;\r
6809   q = errorMessage;\r
6810   while (*p) {\r
6811     if (*p == '\n') {\r
6812       if (modal) {\r
6813         *q++ = ' ';\r
6814         p++;\r
6815       } else {\r
6816         *q++ = '\r';\r
6817         *q++ = *p++;\r
6818       }\r
6819     } else {\r
6820       *q++ = *p++;\r
6821     }\r
6822   }\r
6823   *q = NULLCHAR;\r
6824   strncpy(errorTitle, title, sizeof(errorTitle));\r
6825   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6826   \r
6827   if (modal) {\r
6828     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6829   } else {\r
6830     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6831     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6832                  hwndMain, (DLGPROC)lpProc);\r
6833     FreeProcInstance(lpProc);\r
6834   }\r
6835 }\r
6836 \r
6837 VOID\r
6838 ErrorPopDown()\r
6839 {\r
6840   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6841   if (errorDialog == NULL) return;\r
6842   DestroyWindow(errorDialog);\r
6843   errorDialog = NULL;\r
6844   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6845 }\r
6846 \r
6847 LRESULT CALLBACK\r
6848 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6849 {\r
6850   RECT rChild;\r
6851 \r
6852   switch (message) {\r
6853   case WM_INITDIALOG:\r
6854     GetWindowRect(hDlg, &rChild);\r
6855 \r
6856     /*\r
6857     SetWindowPos(hDlg, NULL, rChild.left,\r
6858       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6859       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6860     */\r
6861 \r
6862     /* \r
6863         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6864         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6865         and it doesn't work when you resize the dialog.\r
6866         For now, just give it a default position.\r
6867     */\r
6868     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6869     Translate(hDlg, DLG_Error);\r
6870 \r
6871     errorDialog = hDlg;\r
6872     SetWindowText(hDlg, errorTitle);\r
6873     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6874     return FALSE;\r
6875 \r
6876   case WM_COMMAND:\r
6877     switch (LOWORD(wParam)) {\r
6878     case IDOK:\r
6879     case IDCANCEL:\r
6880       if (errorDialog == hDlg) errorDialog = NULL;\r
6881       DestroyWindow(hDlg);\r
6882       return TRUE;\r
6883 \r
6884     default:\r
6885       break;\r
6886     }\r
6887     break;\r
6888   }\r
6889   return FALSE;\r
6890 }\r
6891 \r
6892 #ifdef GOTHIC\r
6893 HWND gothicDialog = NULL;\r
6894 \r
6895 LRESULT CALLBACK\r
6896 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6897 {\r
6898   RECT rChild;\r
6899   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6900 \r
6901   switch (message) {\r
6902   case WM_INITDIALOG:\r
6903     GetWindowRect(hDlg, &rChild);\r
6904 \r
6905     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6906                                                              SWP_NOZORDER);\r
6907 \r
6908     /* \r
6909         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6910         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6911         and it doesn't work when you resize the dialog.\r
6912         For now, just give it a default position.\r
6913     */\r
6914     gothicDialog = hDlg;\r
6915     SetWindowText(hDlg, errorTitle);\r
6916     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6917     return FALSE;\r
6918 \r
6919   case WM_COMMAND:\r
6920     switch (LOWORD(wParam)) {\r
6921     case IDOK:\r
6922     case IDCANCEL:\r
6923       if (errorDialog == hDlg) errorDialog = NULL;\r
6924       DestroyWindow(hDlg);\r
6925       return TRUE;\r
6926 \r
6927     default:\r
6928       break;\r
6929     }\r
6930     break;\r
6931   }\r
6932   return FALSE;\r
6933 }\r
6934 \r
6935 VOID\r
6936 GothicPopUp(char *title, VariantClass variant)\r
6937 {\r
6938   FARPROC lpProc;\r
6939   static char *lastTitle;\r
6940 \r
6941   strncpy(errorTitle, title, sizeof(errorTitle));\r
6942   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6943 \r
6944   if(lastTitle != title && gothicDialog != NULL) {\r
6945     DestroyWindow(gothicDialog);\r
6946     gothicDialog = NULL;\r
6947   }\r
6948   if(variant != VariantNormal && gothicDialog == NULL) {\r
6949     title = lastTitle;\r
6950     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6951     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6952                  hwndMain, (DLGPROC)lpProc);\r
6953     FreeProcInstance(lpProc);\r
6954   }\r
6955 }\r
6956 #endif\r
6957 \r
6958 /*---------------------------------------------------------------------------*\\r
6959  *\r
6960  *  Ics Interaction console functions\r
6961  *\r
6962 \*---------------------------------------------------------------------------*/\r
6963 \r
6964 #define HISTORY_SIZE 64\r
6965 static char *history[HISTORY_SIZE];\r
6966 int histIn = 0, histP = 0;\r
6967 \r
6968 \r
6969 VOID\r
6970 SaveInHistory(char *cmd)\r
6971 {\r
6972   if (history[histIn] != NULL) {\r
6973     free(history[histIn]);\r
6974     history[histIn] = NULL;\r
6975   }\r
6976   if (*cmd == NULLCHAR) return;\r
6977   history[histIn] = StrSave(cmd);\r
6978   histIn = (histIn + 1) % HISTORY_SIZE;\r
6979   if (history[histIn] != NULL) {\r
6980     free(history[histIn]);\r
6981 \r
6982     history[histIn] = NULL;\r
6983   }\r
6984   histP = histIn;\r
6985 }\r
6986 \r
6987 char *\r
6988 PrevInHistory(char *cmd)\r
6989 {\r
6990   int newhp;\r
6991   if (histP == histIn) {\r
6992     if (history[histIn] != NULL) free(history[histIn]);\r
6993     history[histIn] = StrSave(cmd);\r
6994   }\r
6995   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6996   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6997   histP = newhp;\r
6998   return history[histP];\r
6999 }\r
7000 \r
7001 char *\r
7002 NextInHistory()\r
7003 {\r
7004   if (histP == histIn) return NULL;\r
7005   histP = (histP + 1) % HISTORY_SIZE;\r
7006   return history[histP];   \r
7007 }\r
7008 \r
7009 HMENU\r
7010 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7011 {\r
7012   HMENU hmenu, h;\r
7013   int i = 0;\r
7014   hmenu = LoadMenu(hInst, "TextMenu");\r
7015   h = GetSubMenu(hmenu, 0);\r
7016   while (e->item) {\r
7017     if (strcmp(e->item, "-") == 0) {\r
7018       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7019     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
7020       int flags = MF_STRING, j = 0;\r
7021       if (e->item[0] == '|') {\r
7022         flags |= MF_MENUBARBREAK;\r
7023         j++;\r
7024       }\r
7025       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
7026       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
7027     }\r
7028     e++;\r
7029     i++;\r
7030   } \r
7031   return hmenu;\r
7032 }\r
7033 \r
7034 WNDPROC consoleTextWindowProc;\r
7035 \r
7036 void\r
7037 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7038 {\r
7039   char buf[MSG_SIZ], name[MSG_SIZ];\r
7040   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7041   CHARRANGE sel;\r
7042 \r
7043   if (!getname) {\r
7044     SetWindowText(hInput, command);\r
7045     if (immediate) {\r
7046       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7047     } else {\r
7048       sel.cpMin = 999999;\r
7049       sel.cpMax = 999999;\r
7050       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7051       SetFocus(hInput);\r
7052     }\r
7053     return;\r
7054   }    \r
7055   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7056   if (sel.cpMin == sel.cpMax) {\r
7057     /* Expand to surrounding word */\r
7058     TEXTRANGE tr;\r
7059     do {\r
7060       tr.chrg.cpMax = sel.cpMin;\r
7061       tr.chrg.cpMin = --sel.cpMin;\r
7062       if (sel.cpMin < 0) break;\r
7063       tr.lpstrText = name;\r
7064       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7065     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7066     sel.cpMin++;\r
7067 \r
7068     do {\r
7069       tr.chrg.cpMin = sel.cpMax;\r
7070       tr.chrg.cpMax = ++sel.cpMax;\r
7071       tr.lpstrText = name;\r
7072       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7073     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7074     sel.cpMax--;\r
7075 \r
7076     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7077       MessageBeep(MB_ICONEXCLAMATION);\r
7078       return;\r
7079     }\r
7080     tr.chrg = sel;\r
7081     tr.lpstrText = name;\r
7082     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7083   } else {\r
7084     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7085       MessageBeep(MB_ICONEXCLAMATION);\r
7086       return;\r
7087     }\r
7088     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7089   }\r
7090   if (immediate) {\r
7091     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
7092     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
7093     SetWindowText(hInput, buf);\r
7094     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7095   } else {\r
7096     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
7097       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
7098     SetWindowText(hInput, buf);\r
7099     sel.cpMin = 999999;\r
7100     sel.cpMax = 999999;\r
7101     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7102     SetFocus(hInput);\r
7103   }\r
7104 }\r
7105 \r
7106 LRESULT CALLBACK \r
7107 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7108 {\r
7109   HWND hInput;\r
7110   CHARRANGE sel;\r
7111 \r
7112   switch (message) {\r
7113   case WM_KEYDOWN:\r
7114     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7115     if(wParam=='R') return 0;\r
7116     switch (wParam) {\r
7117     case VK_PRIOR:\r
7118       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7119       return 0;\r
7120     case VK_NEXT:\r
7121       sel.cpMin = 999999;\r
7122       sel.cpMax = 999999;\r
7123       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7124       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7125       return 0;\r
7126     }\r
7127     break;\r
7128   case WM_CHAR:\r
7129    if(wParam != '\022') {\r
7130     if (wParam == '\t') {\r
7131       if (GetKeyState(VK_SHIFT) < 0) {\r
7132         /* shifted */\r
7133         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7134         if (buttonDesc[0].hwnd) {\r
7135           SetFocus(buttonDesc[0].hwnd);\r
7136         } else {\r
7137           SetFocus(hwndMain);\r
7138         }\r
7139       } else {\r
7140         /* unshifted */\r
7141         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7142       }\r
7143     } else {\r
7144       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7145       JAWS_DELETE( SetFocus(hInput); )\r
7146       SendMessage(hInput, message, wParam, lParam);\r
7147     }\r
7148     return 0;\r
7149    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
7150    lParam = -1;\r
7151   case WM_RBUTTONDOWN:\r
7152     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7153       /* Move selection here if it was empty */\r
7154       POINT pt;\r
7155       pt.x = LOWORD(lParam);\r
7156       pt.y = HIWORD(lParam);\r
7157       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7158       if (sel.cpMin == sel.cpMax) {\r
7159         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7160         sel.cpMax = sel.cpMin;\r
7161         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7162       }\r
7163       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7164 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
7165       POINT pt;\r
7166       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7167       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7168       if (sel.cpMin == sel.cpMax) {\r
7169         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7170         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7171       }\r
7172       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7173         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7174       }\r
7175       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
7176       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
7177       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
7178       MenuPopup(hwnd, pt, hmenu, -1);\r
7179 }\r
7180     }\r
7181     return 0;\r
7182   case WM_RBUTTONUP:\r
7183     if (GetKeyState(VK_SHIFT) & ~1) {\r
7184       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7185         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7186     }\r
7187     return 0;\r
7188   case WM_PASTE:\r
7189     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7190     SetFocus(hInput);\r
7191     return SendMessage(hInput, message, wParam, lParam);\r
7192   case WM_MBUTTONDOWN:\r
7193     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7194   case WM_COMMAND:\r
7195     switch (LOWORD(wParam)) {\r
7196     case IDM_QuickPaste:\r
7197       {\r
7198         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7199         if (sel.cpMin == sel.cpMax) {\r
7200           MessageBeep(MB_ICONEXCLAMATION);\r
7201           return 0;\r
7202         }\r
7203         SendMessage(hwnd, WM_COPY, 0, 0);\r
7204         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7205         SendMessage(hInput, WM_PASTE, 0, 0);\r
7206         SetFocus(hInput);\r
7207         return 0;\r
7208       }\r
7209     case IDM_Cut:\r
7210       SendMessage(hwnd, WM_CUT, 0, 0);\r
7211       return 0;\r
7212     case IDM_Paste:\r
7213       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7214       return 0;\r
7215     case IDM_Copy:\r
7216       SendMessage(hwnd, WM_COPY, 0, 0);\r
7217       return 0;\r
7218     default:\r
7219       {\r
7220         int i = LOWORD(wParam) - IDM_CommandX;\r
7221         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7222             icsTextMenuEntry[i].command != NULL) {\r
7223           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7224                    icsTextMenuEntry[i].getname,\r
7225                    icsTextMenuEntry[i].immediate);\r
7226           return 0;\r
7227         }\r
7228       }\r
7229       break;\r
7230     }\r
7231     break;\r
7232   }\r
7233   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7234 }\r
7235 \r
7236 WNDPROC consoleInputWindowProc;\r
7237 \r
7238 LRESULT CALLBACK\r
7239 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7240 {\r
7241   char buf[MSG_SIZ];\r
7242   char *p;\r
7243   static BOOL sendNextChar = FALSE;\r
7244   static BOOL quoteNextChar = FALSE;\r
7245   InputSource *is = consoleInputSource;\r
7246   CHARFORMAT cf;\r
7247   CHARRANGE sel;\r
7248 \r
7249   switch (message) {\r
7250   case WM_CHAR:\r
7251     if (!appData.localLineEditing || sendNextChar) {\r
7252       is->buf[0] = (CHAR) wParam;\r
7253       is->count = 1;\r
7254       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7255       sendNextChar = FALSE;\r
7256       return 0;\r
7257     }\r
7258     if (quoteNextChar) {\r
7259       buf[0] = (char) wParam;\r
7260       buf[1] = NULLCHAR;\r
7261       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7262       quoteNextChar = FALSE;\r
7263       return 0;\r
7264     }\r
7265     switch (wParam) {\r
7266     case '\r':   /* Enter key */\r
7267       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7268       if (consoleEcho) SaveInHistory(is->buf);\r
7269       is->buf[is->count++] = '\n';\r
7270       is->buf[is->count] = NULLCHAR;\r
7271       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7272       if (consoleEcho) {\r
7273         ConsoleOutput(is->buf, is->count, TRUE);\r
7274       } else if (appData.localLineEditing) {\r
7275         ConsoleOutput("\n", 1, TRUE);\r
7276       }\r
7277       /* fall thru */\r
7278     case '\033': /* Escape key */\r
7279       SetWindowText(hwnd, "");\r
7280       cf.cbSize = sizeof(CHARFORMAT);\r
7281       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7282       if (consoleEcho) {\r
7283         cf.crTextColor = textAttribs[ColorNormal].color;\r
7284       } else {\r
7285         cf.crTextColor = COLOR_ECHOOFF;\r
7286       }\r
7287       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7288       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7289       return 0;\r
7290     case '\t':   /* Tab key */\r
7291       if (GetKeyState(VK_SHIFT) < 0) {\r
7292         /* shifted */\r
7293         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7294       } else {\r
7295         /* unshifted */\r
7296         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7297         if (buttonDesc[0].hwnd) {\r
7298           SetFocus(buttonDesc[0].hwnd);\r
7299         } else {\r
7300           SetFocus(hwndMain);\r
7301         }\r
7302       }\r
7303       return 0;\r
7304     case '\023': /* Ctrl+S */\r
7305       sendNextChar = TRUE;\r
7306       return 0;\r
7307     case '\021': /* Ctrl+Q */\r
7308       quoteNextChar = TRUE;\r
7309       return 0;\r
7310     JAWS_REPLAY\r
7311     default:\r
7312       break;\r
7313     }\r
7314     break;\r
7315   case WM_KEYDOWN:\r
7316     switch (wParam) {\r
7317     case VK_UP:\r
7318       GetWindowText(hwnd, buf, MSG_SIZ);\r
7319       p = PrevInHistory(buf);\r
7320       if (p != NULL) {\r
7321         SetWindowText(hwnd, p);\r
7322         sel.cpMin = 999999;\r
7323         sel.cpMax = 999999;\r
7324         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7325         return 0;\r
7326       }\r
7327       break;\r
7328     case VK_DOWN:\r
7329       p = NextInHistory();\r
7330       if (p != NULL) {\r
7331         SetWindowText(hwnd, p);\r
7332         sel.cpMin = 999999;\r
7333         sel.cpMax = 999999;\r
7334         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7335         return 0;\r
7336       }\r
7337       break;\r
7338     case VK_HOME:\r
7339     case VK_END:\r
7340       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7341       /* fall thru */\r
7342     case VK_PRIOR:\r
7343     case VK_NEXT:\r
7344       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7345       return 0;\r
7346     }\r
7347     break;\r
7348   case WM_MBUTTONDOWN:\r
7349     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7350       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7351     break;\r
7352   case WM_RBUTTONUP:\r
7353     if (GetKeyState(VK_SHIFT) & ~1) {\r
7354       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7355         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7356     } else {\r
7357       POINT pt;\r
7358       HMENU hmenu;\r
7359       hmenu = LoadMenu(hInst, "InputMenu");\r
7360       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7361       if (sel.cpMin == sel.cpMax) {\r
7362         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7363         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7364       }\r
7365       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7366         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7367       }\r
7368       pt.x = LOWORD(lParam);\r
7369       pt.y = HIWORD(lParam);\r
7370       MenuPopup(hwnd, pt, hmenu, -1);\r
7371     }\r
7372     return 0;\r
7373   case WM_COMMAND:\r
7374     switch (LOWORD(wParam)) { \r
7375     case IDM_Undo:\r
7376       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7377       return 0;\r
7378     case IDM_SelectAll:\r
7379       sel.cpMin = 0;\r
7380       sel.cpMax = -1; /*999999?*/\r
7381       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7382       return 0;\r
7383     case IDM_Cut:\r
7384       SendMessage(hwnd, WM_CUT, 0, 0);\r
7385       return 0;\r
7386     case IDM_Paste:\r
7387       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7388       return 0;\r
7389     case IDM_Copy:\r
7390       SendMessage(hwnd, WM_COPY, 0, 0);\r
7391       return 0;\r
7392     }\r
7393     break;\r
7394   }\r
7395   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7396 }\r
7397 \r
7398 #define CO_MAX  100000\r
7399 #define CO_TRIM   1000\r
7400 \r
7401 LRESULT CALLBACK\r
7402 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7403 {\r
7404   static SnapData sd;\r
7405   HWND hText, hInput;\r
7406   RECT rect;\r
7407   static int sizeX, sizeY;\r
7408   int newSizeX, newSizeY;\r
7409   MINMAXINFO *mmi;\r
7410   WORD wMask;\r
7411 \r
7412   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7413   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7414 \r
7415   switch (message) {\r
7416   case WM_NOTIFY:\r
7417     if (((NMHDR*)lParam)->code == EN_LINK)\r
7418     {\r
7419       ENLINK *pLink = (ENLINK*)lParam;\r
7420       if (pLink->msg == WM_LBUTTONUP)\r
7421       {\r
7422         TEXTRANGE tr;\r
7423 \r
7424         tr.chrg = pLink->chrg;\r
7425         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7426         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7427         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7428         free(tr.lpstrText);\r
7429       }\r
7430     }\r
7431     break;\r
7432   case WM_INITDIALOG: /* message: initialize dialog box */\r
7433     hwndConsole = hDlg;\r
7434     SetFocus(hInput);\r
7435     consoleTextWindowProc = (WNDPROC)\r
7436       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7437     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7438     consoleInputWindowProc = (WNDPROC)\r
7439       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7440     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7441     Colorize(ColorNormal, TRUE);\r
7442     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7443     ChangedConsoleFont();\r
7444     GetClientRect(hDlg, &rect);\r
7445     sizeX = rect.right;\r
7446     sizeY = rect.bottom;\r
7447     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7448         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7449       WINDOWPLACEMENT wp;\r
7450       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7451       wp.length = sizeof(WINDOWPLACEMENT);\r
7452       wp.flags = 0;\r
7453       wp.showCmd = SW_SHOW;\r
7454       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7455       wp.rcNormalPosition.left = wpConsole.x;\r
7456       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7457       wp.rcNormalPosition.top = wpConsole.y;\r
7458       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7459       SetWindowPlacement(hDlg, &wp);\r
7460     }\r
7461 \r
7462    // [HGM] Chessknight's change 2004-07-13\r
7463    else { /* Determine Defaults */\r
7464        WINDOWPLACEMENT wp;\r
7465        wpConsole.x = wpMain.width + 1;\r
7466        wpConsole.y = wpMain.y;\r
7467        wpConsole.width = screenWidth -  wpMain.width;\r
7468        wpConsole.height = wpMain.height;\r
7469        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7470        wp.length = sizeof(WINDOWPLACEMENT);\r
7471        wp.flags = 0;\r
7472        wp.showCmd = SW_SHOW;\r
7473        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7474        wp.rcNormalPosition.left = wpConsole.x;\r
7475        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7476        wp.rcNormalPosition.top = wpConsole.y;\r
7477        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7478        SetWindowPlacement(hDlg, &wp);\r
7479     }\r
7480 \r
7481    // Allow hText to highlight URLs and send notifications on them\r
7482    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7483    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7484    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7485    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7486 \r
7487     return FALSE;\r
7488 \r
7489   case WM_SETFOCUS:\r
7490     SetFocus(hInput);\r
7491     return 0;\r
7492 \r
7493   case WM_CLOSE:\r
7494     ExitEvent(0);\r
7495     /* not reached */\r
7496     break;\r
7497 \r
7498   case WM_SIZE:\r
7499     if (IsIconic(hDlg)) break;\r
7500     newSizeX = LOWORD(lParam);\r
7501     newSizeY = HIWORD(lParam);\r
7502     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7503       RECT rectText, rectInput;\r
7504       POINT pt;\r
7505       int newTextHeight, newTextWidth;\r
7506       GetWindowRect(hText, &rectText);\r
7507       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7508       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7509       if (newTextHeight < 0) {\r
7510         newSizeY += -newTextHeight;\r
7511         newTextHeight = 0;\r
7512       }\r
7513       SetWindowPos(hText, NULL, 0, 0,\r
7514         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7515       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7516       pt.x = rectInput.left;\r
7517       pt.y = rectInput.top + newSizeY - sizeY;\r
7518       ScreenToClient(hDlg, &pt);\r
7519       SetWindowPos(hInput, NULL, \r
7520         pt.x, pt.y, /* needs client coords */   \r
7521         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7522         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7523     }\r
7524     sizeX = newSizeX;\r
7525     sizeY = newSizeY;\r
7526     break;\r
7527 \r
7528   case WM_GETMINMAXINFO:\r
7529     /* Prevent resizing window too small */\r
7530     mmi = (MINMAXINFO *) lParam;\r
7531     mmi->ptMinTrackSize.x = 100;\r
7532     mmi->ptMinTrackSize.y = 100;\r
7533     break;\r
7534 \r
7535   /* [AS] Snapping */\r
7536   case WM_ENTERSIZEMOVE:\r
7537     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7538 \r
7539   case WM_SIZING:\r
7540     return OnSizing( &sd, hDlg, wParam, lParam );\r
7541 \r
7542   case WM_MOVING:\r
7543     return OnMoving( &sd, hDlg, wParam, lParam );\r
7544 \r
7545   case WM_EXITSIZEMOVE:\r
7546         UpdateICSWidth(hText);\r
7547     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7548   }\r
7549 \r
7550   return DefWindowProc(hDlg, message, wParam, lParam);\r
7551 }\r
7552 \r
7553 \r
7554 VOID\r
7555 ConsoleCreate()\r
7556 {\r
7557   HWND hCons;\r
7558   if (hwndConsole) return;\r
7559   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7560   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7561 }\r
7562 \r
7563 \r
7564 VOID\r
7565 ConsoleOutput(char* data, int length, int forceVisible)\r
7566 {\r
7567   HWND hText;\r
7568   int trim, exlen;\r
7569   char *p, *q;\r
7570   char buf[CO_MAX+1];\r
7571   POINT pEnd;\r
7572   RECT rect;\r
7573   static int delayLF = 0;\r
7574   CHARRANGE savesel, sel;\r
7575 \r
7576   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7577   p = data;\r
7578   q = buf;\r
7579   if (delayLF) {\r
7580     *q++ = '\r';\r
7581     *q++ = '\n';\r
7582     delayLF = 0;\r
7583   }\r
7584   while (length--) {\r
7585     if (*p == '\n') {\r
7586       if (*++p) {\r
7587         *q++ = '\r';\r
7588         *q++ = '\n';\r
7589       } else {\r
7590         delayLF = 1;\r
7591       }\r
7592     } else if (*p == '\007') {\r
7593        MyPlaySound(&sounds[(int)SoundBell]);\r
7594        p++;\r
7595     } else {\r
7596       *q++ = *p++;\r
7597     }\r
7598   }\r
7599   *q = NULLCHAR;\r
7600   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7601   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7602   /* Save current selection */\r
7603   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7604   exlen = GetWindowTextLength(hText);\r
7605   /* Find out whether current end of text is visible */\r
7606   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7607   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7608   /* Trim existing text if it's too long */\r
7609   if (exlen + (q - buf) > CO_MAX) {\r
7610     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7611     sel.cpMin = 0;\r
7612     sel.cpMax = trim;\r
7613     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7614     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7615     exlen -= trim;\r
7616     savesel.cpMin -= trim;\r
7617     savesel.cpMax -= trim;\r
7618     if (exlen < 0) exlen = 0;\r
7619     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7620     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7621   }\r
7622   /* Append the new text */\r
7623   sel.cpMin = exlen;\r
7624   sel.cpMax = exlen;\r
7625   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7626   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7627   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7628   if (forceVisible || exlen == 0 ||\r
7629       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7630        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7631     /* Scroll to make new end of text visible if old end of text\r
7632        was visible or new text is an echo of user typein */\r
7633     sel.cpMin = 9999999;\r
7634     sel.cpMax = 9999999;\r
7635     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7636     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7637     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7638     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7639   }\r
7640   if (savesel.cpMax == exlen || forceVisible) {\r
7641     /* Move insert point to new end of text if it was at the old\r
7642        end of text or if the new text is an echo of user typein */\r
7643     sel.cpMin = 9999999;\r
7644     sel.cpMax = 9999999;\r
7645     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7646   } else {\r
7647     /* Restore previous selection */\r
7648     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7649   }\r
7650   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7651 }\r
7652 \r
7653 /*---------*/\r
7654 \r
7655 \r
7656 void\r
7657 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7658 {\r
7659   char buf[100];\r
7660   char *str;\r
7661   COLORREF oldFg, oldBg;\r
7662   HFONT oldFont;\r
7663   RECT rect;\r
7664 \r
7665   if(copyNumber > 1)\r
7666     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7667 \r
7668   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7669   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7670   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7671 \r
7672   rect.left = x;\r
7673   rect.right = x + squareSize;\r
7674   rect.top  = y;\r
7675   rect.bottom = y + squareSize;\r
7676   str = buf;\r
7677 \r
7678   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7679                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7680              y, ETO_CLIPPED|ETO_OPAQUE,\r
7681              &rect, str, strlen(str), NULL);\r
7682 \r
7683   (void) SetTextColor(hdc, oldFg);\r
7684   (void) SetBkColor(hdc, oldBg);\r
7685   (void) SelectObject(hdc, oldFont);\r
7686 }\r
7687 \r
7688 void\r
7689 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7690               RECT *rect, char *color, char *flagFell)\r
7691 {\r
7692   char buf[100];\r
7693   char *str;\r
7694   COLORREF oldFg, oldBg;\r
7695   HFONT oldFont;\r
7696 \r
7697   if (twoBoards && partnerUp) return;\r
7698   if (appData.clockMode) {\r
7699     if (tinyLayout == 2)\r
7700       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7701     else\r
7702       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7703     str = buf;\r
7704   } else {\r
7705     str = color;\r
7706   }\r
7707 \r
7708   if (highlight) {\r
7709     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7710     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7711   } else {\r
7712     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7713     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7714   }\r
7715 \r
7716   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7717 \r
7718   JAWS_SILENCE\r
7719 \r
7720   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7721              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7722              rect, str, strlen(str), NULL);\r
7723   if(logoHeight > 0 && appData.clockMode) {\r
7724       RECT r;\r
7725       str += strlen(color)+2;\r
7726       r.top = rect->top + logoHeight/2;\r
7727       r.left = rect->left;\r
7728       r.right = rect->right;\r
7729       r.bottom = rect->bottom;\r
7730       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7731                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7732                  &r, str, strlen(str), NULL);\r
7733   }\r
7734   (void) SetTextColor(hdc, oldFg);\r
7735   (void) SetBkColor(hdc, oldBg);\r
7736   (void) SelectObject(hdc, oldFont);\r
7737 }\r
7738 \r
7739 \r
7740 int\r
7741 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7742            OVERLAPPED *ovl)\r
7743 {\r
7744   int ok, err;\r
7745 \r
7746   /* [AS]  */\r
7747   if( count <= 0 ) {\r
7748     if (appData.debugMode) {\r
7749       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7750     }\r
7751 \r
7752     return ERROR_INVALID_USER_BUFFER;\r
7753   }\r
7754 \r
7755   ResetEvent(ovl->hEvent);\r
7756   ovl->Offset = ovl->OffsetHigh = 0;\r
7757   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7758   if (ok) {\r
7759     err = NO_ERROR;\r
7760   } else {\r
7761     err = GetLastError();\r
7762     if (err == ERROR_IO_PENDING) {\r
7763       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7764       if (ok)\r
7765         err = NO_ERROR;\r
7766       else\r
7767         err = GetLastError();\r
7768     }\r
7769   }\r
7770   return err;\r
7771 }\r
7772 \r
7773 int\r
7774 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7775             OVERLAPPED *ovl)\r
7776 {\r
7777   int ok, err;\r
7778 \r
7779   ResetEvent(ovl->hEvent);\r
7780   ovl->Offset = ovl->OffsetHigh = 0;\r
7781   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7782   if (ok) {\r
7783     err = NO_ERROR;\r
7784   } else {\r
7785     err = GetLastError();\r
7786     if (err == ERROR_IO_PENDING) {\r
7787       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7788       if (ok)\r
7789         err = NO_ERROR;\r
7790       else\r
7791         err = GetLastError();\r
7792     }\r
7793 \r
7794   }\r
7795   return err;\r
7796 }\r
7797 \r
7798 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7799 void CheckForInputBufferFull( InputSource * is )\r
7800 {\r
7801     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7802         /* Look for end of line */\r
7803         char * p = is->buf;\r
7804         \r
7805         while( p < is->next && *p != '\n' ) {\r
7806             p++;\r
7807         }\r
7808 \r
7809         if( p >= is->next ) {\r
7810             if (appData.debugMode) {\r
7811                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7812             }\r
7813 \r
7814             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7815             is->count = (DWORD) -1;\r
7816             is->next = is->buf;\r
7817         }\r
7818     }\r
7819 }\r
7820 \r
7821 DWORD\r
7822 InputThread(LPVOID arg)\r
7823 {\r
7824   InputSource *is;\r
7825   OVERLAPPED ovl;\r
7826 \r
7827   is = (InputSource *) arg;\r
7828   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7829   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7830   while (is->hThread != NULL) {\r
7831     is->error = DoReadFile(is->hFile, is->next,\r
7832                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7833                            &is->count, &ovl);\r
7834     if (is->error == NO_ERROR) {\r
7835       is->next += is->count;\r
7836     } else {\r
7837       if (is->error == ERROR_BROKEN_PIPE) {\r
7838         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7839         is->count = 0;\r
7840       } else {\r
7841         is->count = (DWORD) -1;\r
7842         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7843         break; \r
7844       }\r
7845     }\r
7846 \r
7847     CheckForInputBufferFull( is );\r
7848 \r
7849     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7850 \r
7851     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7852 \r
7853     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7854   }\r
7855 \r
7856   CloseHandle(ovl.hEvent);\r
7857   CloseHandle(is->hFile);\r
7858 \r
7859   if (appData.debugMode) {\r
7860     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7861   }\r
7862 \r
7863   return 0;\r
7864 }\r
7865 \r
7866 \r
7867 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7868 DWORD\r
7869 NonOvlInputThread(LPVOID arg)\r
7870 {\r
7871   InputSource *is;\r
7872   char *p, *q;\r
7873   int i;\r
7874   char prev;\r
7875 \r
7876   is = (InputSource *) arg;\r
7877   while (is->hThread != NULL) {\r
7878     is->error = ReadFile(is->hFile, is->next,\r
7879                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7880                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7881     if (is->error == NO_ERROR) {\r
7882       /* Change CRLF to LF */\r
7883       if (is->next > is->buf) {\r
7884         p = is->next - 1;\r
7885         i = is->count + 1;\r
7886       } else {\r
7887         p = is->next;\r
7888         i = is->count;\r
7889       }\r
7890       q = p;\r
7891       prev = NULLCHAR;\r
7892       while (i > 0) {\r
7893         if (prev == '\r' && *p == '\n') {\r
7894           *(q-1) = '\n';\r
7895           is->count--;\r
7896         } else { \r
7897           *q++ = *p;\r
7898         }\r
7899         prev = *p++;\r
7900         i--;\r
7901       }\r
7902       *q = NULLCHAR;\r
7903       is->next = q;\r
7904     } else {\r
7905       if (is->error == ERROR_BROKEN_PIPE) {\r
7906         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7907         is->count = 0; \r
7908       } else {\r
7909         is->count = (DWORD) -1;\r
7910       }\r
7911     }\r
7912 \r
7913     CheckForInputBufferFull( is );\r
7914 \r
7915     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7916 \r
7917     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7918 \r
7919     if (is->count < 0) break;  /* Quit on error */\r
7920   }\r
7921   CloseHandle(is->hFile);\r
7922   return 0;\r
7923 }\r
7924 \r
7925 DWORD\r
7926 SocketInputThread(LPVOID arg)\r
7927 {\r
7928   InputSource *is;\r
7929 \r
7930   is = (InputSource *) arg;\r
7931   while (is->hThread != NULL) {\r
7932     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7933     if ((int)is->count == SOCKET_ERROR) {\r
7934       is->count = (DWORD) -1;\r
7935       is->error = WSAGetLastError();\r
7936     } else {\r
7937       is->error = NO_ERROR;\r
7938       is->next += is->count;\r
7939       if (is->count == 0 && is->second == is) {\r
7940         /* End of file on stderr; quit with no message */\r
7941         break;\r
7942       }\r
7943     }\r
7944     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7945 \r
7946     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7947 \r
7948     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7949   }\r
7950   return 0;\r
7951 }\r
7952 \r
7953 VOID\r
7954 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7955 {\r
7956   InputSource *is;\r
7957 \r
7958   is = (InputSource *) lParam;\r
7959   if (is->lineByLine) {\r
7960     /* Feed in lines one by one */\r
7961     char *p = is->buf;\r
7962     char *q = p;\r
7963     while (q < is->next) {\r
7964       if (*q++ == '\n') {\r
7965         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7966         p = q;\r
7967       }\r
7968     }\r
7969     \r
7970     /* Move any partial line to the start of the buffer */\r
7971     q = is->buf;\r
7972     while (p < is->next) {\r
7973       *q++ = *p++;\r
7974     }\r
7975     is->next = q;\r
7976 \r
7977     if (is->error != NO_ERROR || is->count == 0) {\r
7978       /* Notify backend of the error.  Note: If there was a partial\r
7979          line at the end, it is not flushed through. */\r
7980       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7981     }\r
7982   } else {\r
7983     /* Feed in the whole chunk of input at once */\r
7984     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7985     is->next = is->buf;\r
7986   }\r
7987 }\r
7988 \r
7989 /*---------------------------------------------------------------------------*\\r
7990  *\r
7991  *  Menu enables. Used when setting various modes.\r
7992  *\r
7993 \*---------------------------------------------------------------------------*/\r
7994 \r
7995 typedef struct {\r
7996   int item;\r
7997   int flags;\r
7998 } Enables;\r
7999 \r
8000 VOID\r
8001 GreyRevert(Boolean grey)\r
8002 { // [HGM] vari: for retracting variations in local mode\r
8003   HMENU hmenu = GetMenu(hwndMain);\r
8004   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
8005   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
8006 }\r
8007 \r
8008 VOID\r
8009 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8010 {\r
8011   while (enab->item > 0) {\r
8012     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8013     enab++;\r
8014   }\r
8015 }\r
8016 \r
8017 Enables gnuEnables[] = {\r
8018   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8019   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8020   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8021   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8022   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8023   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8024   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8025   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8026   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8027   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
8028   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8029   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8030   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8031 \r
8032 \r
8033   // Needed to switch from ncp to GNU mode on Engine Load\r
8034   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8035   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8036   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8037   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8038   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8039   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8040   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
8041   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
8042   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
8043   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
8044   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8045   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8046   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8047   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8048   { -1, -1 }\r
8049 };\r
8050 \r
8051 Enables icsEnables[] = {\r
8052   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8053   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8054   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8055   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8056   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8057   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8058   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8059   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8060   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8061   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8062   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8063   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8064   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8065   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
8066   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
8067   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8068   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8069   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8070   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8071   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
8072   { -1, -1 }\r
8073 };\r
8074 \r
8075 #if ZIPPY\r
8076 Enables zippyEnables[] = {\r
8077   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8078   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8079   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8080   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
8081   { -1, -1 }\r
8082 };\r
8083 #endif\r
8084 \r
8085 Enables ncpEnables[] = {\r
8086   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8087   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8088   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8089   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8090   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8091   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8092   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8093   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8094   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8095   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8096   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8097   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8098   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8099   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8100   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8101   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8102   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8103   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8104   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8105   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8106   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8107   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
8108   { -1, -1 }\r
8109 };\r
8110 \r
8111 Enables trainingOnEnables[] = {\r
8112   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8113   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
8114   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8115   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8116   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8117   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8118   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8119   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8120   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8121   { -1, -1 }\r
8122 };\r
8123 \r
8124 Enables trainingOffEnables[] = {\r
8125   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8126   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
8127   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8128   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8129   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8130   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8131   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8132   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8133   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8134   { -1, -1 }\r
8135 };\r
8136 \r
8137 /* These modify either ncpEnables or gnuEnables */\r
8138 Enables cmailEnables[] = {\r
8139   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8140   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8141   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8142   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8143   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8144   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8145   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8146   { -1, -1 }\r
8147 };\r
8148 \r
8149 Enables machineThinkingEnables[] = {\r
8150   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8151   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8152   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8153   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8154   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8155   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8156   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8157   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8158   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8159   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8160   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8161   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8162   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8163 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8164   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8165   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8166   { -1, -1 }\r
8167 };\r
8168 \r
8169 Enables userThinkingEnables[] = {\r
8170   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8171   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8172   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8173   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8174   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8175   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8176   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8177   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8178   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8179   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8180   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8181   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8182   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8183 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8184   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8185   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8186   { -1, -1 }\r
8187 };\r
8188 \r
8189 /*---------------------------------------------------------------------------*\\r
8190  *\r
8191  *  Front-end interface functions exported by XBoard.\r
8192  *  Functions appear in same order as prototypes in frontend.h.\r
8193  * \r
8194 \*---------------------------------------------------------------------------*/\r
8195 VOID\r
8196 CheckMark(UINT item, int state)\r
8197 {\r
8198     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
8199 }\r
8200 \r
8201 VOID\r
8202 ModeHighlight()\r
8203 {\r
8204   static UINT prevChecked = 0;\r
8205   static int prevPausing = 0;\r
8206   UINT nowChecked;\r
8207 \r
8208   if (pausing != prevPausing) {\r
8209     prevPausing = pausing;\r
8210     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8211                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8212     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8213   }\r
8214 \r
8215   switch (gameMode) {\r
8216   case BeginningOfGame:\r
8217     if (appData.icsActive)\r
8218       nowChecked = IDM_IcsClient;\r
8219     else if (appData.noChessProgram)\r
8220       nowChecked = IDM_EditGame;\r
8221     else\r
8222       nowChecked = IDM_MachineBlack;\r
8223     break;\r
8224   case MachinePlaysBlack:\r
8225     nowChecked = IDM_MachineBlack;\r
8226     break;\r
8227   case MachinePlaysWhite:\r
8228     nowChecked = IDM_MachineWhite;\r
8229     break;\r
8230   case TwoMachinesPlay:\r
8231     nowChecked = IDM_TwoMachines;\r
8232     break;\r
8233   case AnalyzeMode:\r
8234     nowChecked = IDM_AnalysisMode;\r
8235     break;\r
8236   case AnalyzeFile:\r
8237     nowChecked = IDM_AnalyzeFile;\r
8238     break;\r
8239   case EditGame:\r
8240     nowChecked = IDM_EditGame;\r
8241     break;\r
8242   case PlayFromGameFile:\r
8243     nowChecked = IDM_LoadGame;\r
8244     break;\r
8245   case EditPosition:\r
8246     nowChecked = IDM_EditPosition;\r
8247     break;\r
8248   case Training:\r
8249     nowChecked = IDM_Training;\r
8250     break;\r
8251   case IcsPlayingWhite:\r
8252   case IcsPlayingBlack:\r
8253   case IcsObserving:\r
8254   case IcsIdle:\r
8255     nowChecked = IDM_IcsClient;\r
8256     break;\r
8257   default:\r
8258   case EndOfGame:\r
8259     nowChecked = 0;\r
8260     break;\r
8261   }\r
8262   if(prevChecked == IDM_TwoMachines) // [HGM] 'Machine Match' might have gotten disabled when stopping match\r
8263     EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_ENABLED);\r
8264   CheckMark(prevChecked, MF_UNCHECKED);\r
8265   CheckMark(nowChecked, MF_CHECKED);\r
8266   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8267 \r
8268   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8269     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8270                           MF_BYCOMMAND|MF_ENABLED);\r
8271   } else {\r
8272     (void) EnableMenuItem(GetMenu(hwndMain), \r
8273                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8274   }\r
8275 \r
8276   prevChecked = nowChecked;\r
8277 \r
8278   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8279   if (appData.icsActive) {\r
8280        if (appData.icsEngineAnalyze) {\r
8281                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8282        } else {\r
8283                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8284        }\r
8285   }\r
8286   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8287 }\r
8288 \r
8289 VOID\r
8290 SetICSMode()\r
8291 {\r
8292   HMENU hmenu = GetMenu(hwndMain);\r
8293   SetMenuEnables(hmenu, icsEnables);\r
8294   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8295     MF_BYCOMMAND|MF_ENABLED);\r
8296 #if ZIPPY\r
8297   if (appData.zippyPlay) {\r
8298     SetMenuEnables(hmenu, zippyEnables);\r
8299     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8300          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8301           MF_BYCOMMAND|MF_ENABLED);\r
8302   }\r
8303 #endif\r
8304 }\r
8305 \r
8306 VOID\r
8307 SetGNUMode()\r
8308 {\r
8309   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8310 }\r
8311 \r
8312 VOID\r
8313 SetNCPMode()\r
8314 {\r
8315   HMENU hmenu = GetMenu(hwndMain);\r
8316   SetMenuEnables(hmenu, ncpEnables);\r
8317     DrawMenuBar(hwndMain);\r
8318 }\r
8319 \r
8320 VOID\r
8321 SetCmailMode()\r
8322 {\r
8323   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8324 }\r
8325 \r
8326 VOID \r
8327 SetTrainingModeOn()\r
8328 {\r
8329   int i;\r
8330   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8331   for (i = 0; i < N_BUTTONS; i++) {\r
8332     if (buttonDesc[i].hwnd != NULL)\r
8333       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8334   }\r
8335   CommentPopDown();\r
8336 }\r
8337 \r
8338 VOID SetTrainingModeOff()\r
8339 {\r
8340   int i;\r
8341   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8342   for (i = 0; i < N_BUTTONS; i++) {\r
8343     if (buttonDesc[i].hwnd != NULL)\r
8344       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8345   }\r
8346 }\r
8347 \r
8348 \r
8349 VOID\r
8350 SetUserThinkingEnables()\r
8351 {\r
8352   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8353 }\r
8354 \r
8355 VOID\r
8356 SetMachineThinkingEnables()\r
8357 {\r
8358   HMENU hMenu = GetMenu(hwndMain);\r
8359   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8360 \r
8361   SetMenuEnables(hMenu, machineThinkingEnables);\r
8362 \r
8363   if (gameMode == MachinePlaysBlack) {\r
8364     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8365   } else if (gameMode == MachinePlaysWhite) {\r
8366     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8367   } else if (gameMode == TwoMachinesPlay) {\r
8368     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8369   }\r
8370 }\r
8371 \r
8372 \r
8373 VOID\r
8374 DisplayTitle(char *str)\r
8375 {\r
8376   char title[MSG_SIZ], *host;\r
8377   if (str[0] != NULLCHAR) {\r
8378     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8379   } else if (appData.icsActive) {\r
8380     if (appData.icsCommPort[0] != NULLCHAR)\r
8381       host = "ICS";\r
8382     else \r
8383       host = appData.icsHost;\r
8384       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8385   } else if (appData.noChessProgram) {\r
8386     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8387   } else {\r
8388     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8389     strcat(title, ": ");\r
8390     strcat(title, first.tidy);\r
8391   }\r
8392   SetWindowText(hwndMain, title);\r
8393 }\r
8394 \r
8395 \r
8396 VOID\r
8397 DisplayMessage(char *str1, char *str2)\r
8398 {\r
8399   HDC hdc;\r
8400   HFONT oldFont;\r
8401   int remain = MESSAGE_TEXT_MAX - 1;\r
8402   int len;\r
8403 \r
8404   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8405   messageText[0] = NULLCHAR;\r
8406   if (*str1) {\r
8407     len = strlen(str1);\r
8408     if (len > remain) len = remain;\r
8409     strncpy(messageText, str1, len);\r
8410     messageText[len] = NULLCHAR;\r
8411     remain -= len;\r
8412   }\r
8413   if (*str2 && remain >= 2) {\r
8414     if (*str1) {\r
8415       strcat(messageText, "  ");\r
8416       remain -= 2;\r
8417     }\r
8418     len = strlen(str2);\r
8419     if (len > remain) len = remain;\r
8420     strncat(messageText, str2, len);\r
8421   }\r
8422   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8423   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8424 \r
8425   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8426 \r
8427   SAYMACHINEMOVE();\r
8428 \r
8429   hdc = GetDC(hwndMain);\r
8430   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8431   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8432              &messageRect, messageText, strlen(messageText), NULL);\r
8433   (void) SelectObject(hdc, oldFont);\r
8434   (void) ReleaseDC(hwndMain, hdc);\r
8435 }\r
8436 \r
8437 VOID\r
8438 DisplayError(char *str, int error)\r
8439 {\r
8440   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8441   int len;\r
8442 \r
8443   if (error == 0) {\r
8444     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8445   } else {\r
8446     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8447                         NULL, error, LANG_NEUTRAL,\r
8448                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8449     if (len > 0) {\r
8450       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8451     } else {\r
8452       ErrorMap *em = errmap;\r
8453       while (em->err != 0 && em->err != error) em++;\r
8454       if (em->err != 0) {\r
8455         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8456       } else {\r
8457         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8458       }\r
8459     }\r
8460   }\r
8461   \r
8462   ErrorPopUp(_("Error"), buf);\r
8463 }\r
8464 \r
8465 \r
8466 VOID\r
8467 DisplayMoveError(char *str)\r
8468 {\r
8469   fromX = fromY = -1;\r
8470   ClearHighlights();\r
8471   DrawPosition(FALSE, NULL);\r
8472   if (appData.popupMoveErrors) {\r
8473     ErrorPopUp(_("Error"), str);\r
8474   } else {\r
8475     DisplayMessage(str, "");\r
8476     moveErrorMessageUp = TRUE;\r
8477   }\r
8478 }\r
8479 \r
8480 VOID\r
8481 DisplayFatalError(char *str, int error, int exitStatus)\r
8482 {\r
8483   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8484   int len;\r
8485   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8486 \r
8487   if (error != 0) {\r
8488     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8489                         NULL, error, LANG_NEUTRAL,\r
8490                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8491     if (len > 0) {\r
8492       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8493     } else {\r
8494       ErrorMap *em = errmap;\r
8495       while (em->err != 0 && em->err != error) em++;\r
8496       if (em->err != 0) {\r
8497         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8498       } else {\r
8499         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8500       }\r
8501     }\r
8502     str = buf;\r
8503   }\r
8504   if (appData.debugMode) {\r
8505     fprintf(debugFP, "%s: %s\n", label, str);\r
8506   }\r
8507   if (appData.popupExitMessage) {\r
8508     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8509                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8510   }\r
8511   ExitEvent(exitStatus);\r
8512 }\r
8513 \r
8514 \r
8515 VOID\r
8516 DisplayInformation(char *str)\r
8517 {\r
8518   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8519 }\r
8520 \r
8521 \r
8522 VOID\r
8523 DisplayNote(char *str)\r
8524 {\r
8525   ErrorPopUp(_("Note"), str);\r
8526 }\r
8527 \r
8528 \r
8529 typedef struct {\r
8530   char *title, *question, *replyPrefix;\r
8531   ProcRef pr;\r
8532 } QuestionParams;\r
8533 \r
8534 LRESULT CALLBACK\r
8535 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8536 {\r
8537   static QuestionParams *qp;\r
8538   char reply[MSG_SIZ];\r
8539   int len, err;\r
8540 \r
8541   switch (message) {\r
8542   case WM_INITDIALOG:\r
8543     qp = (QuestionParams *) lParam;\r
8544     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8545     Translate(hDlg, DLG_Question);\r
8546     SetWindowText(hDlg, qp->title);\r
8547     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8548     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8549     return FALSE;\r
8550 \r
8551   case WM_COMMAND:\r
8552     switch (LOWORD(wParam)) {\r
8553     case IDOK:\r
8554       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8555       if (*reply) strcat(reply, " ");\r
8556       len = strlen(reply);\r
8557       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8558       strcat(reply, "\n");\r
8559       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8560       EndDialog(hDlg, TRUE);\r
8561       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8562       return TRUE;\r
8563     case IDCANCEL:\r
8564       EndDialog(hDlg, FALSE);\r
8565       return TRUE;\r
8566     default:\r
8567       break;\r
8568     }\r
8569     break;\r
8570   }\r
8571   return FALSE;\r
8572 }\r
8573 \r
8574 VOID\r
8575 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8576 {\r
8577     QuestionParams qp;\r
8578     FARPROC lpProc;\r
8579     \r
8580     qp.title = title;\r
8581     qp.question = question;\r
8582     qp.replyPrefix = replyPrefix;\r
8583     qp.pr = pr;\r
8584     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8585     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8586       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8587     FreeProcInstance(lpProc);\r
8588 }\r
8589 \r
8590 /* [AS] Pick FRC position */\r
8591 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8592 {\r
8593     static int * lpIndexFRC;\r
8594     BOOL index_is_ok;\r
8595     char buf[16];\r
8596 \r
8597     switch( message )\r
8598     {\r
8599     case WM_INITDIALOG:\r
8600         lpIndexFRC = (int *) lParam;\r
8601 \r
8602         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8603         Translate(hDlg, DLG_NewGameFRC);\r
8604 \r
8605         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8606         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8607         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8608         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8609 \r
8610         break;\r
8611 \r
8612     case WM_COMMAND:\r
8613         switch( LOWORD(wParam) ) {\r
8614         case IDOK:\r
8615             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8616             EndDialog( hDlg, 0 );\r
8617             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8618             return TRUE;\r
8619         case IDCANCEL:\r
8620             EndDialog( hDlg, 1 );   \r
8621             return TRUE;\r
8622         case IDC_NFG_Edit:\r
8623             if( HIWORD(wParam) == EN_CHANGE ) {\r
8624                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8625 \r
8626                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8627             }\r
8628             return TRUE;\r
8629         case IDC_NFG_Random:\r
8630           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8631             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8632             return TRUE;\r
8633         }\r
8634 \r
8635         break;\r
8636     }\r
8637 \r
8638     return FALSE;\r
8639 }\r
8640 \r
8641 int NewGameFRC()\r
8642 {\r
8643     int result;\r
8644     int index = appData.defaultFrcPosition;\r
8645     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8646 \r
8647     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8648 \r
8649     if( result == 0 ) {\r
8650         appData.defaultFrcPosition = index;\r
8651     }\r
8652 \r
8653     return result;\r
8654 }\r
8655 \r
8656 /* [AS] Game list options. Refactored by HGM */\r
8657 \r
8658 HWND gameListOptionsDialog;\r
8659 \r
8660 // low-level front-end: clear text edit / list widget\r
8661 void\r
8662 \r
8663 GLT_ClearList()\r
8664 {\r
8665     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8666 }\r
8667 \r
8668 // low-level front-end: clear text edit / list widget\r
8669 void\r
8670 GLT_DeSelectList()\r
8671 {\r
8672     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8673 }\r
8674 \r
8675 // low-level front-end: append line to text edit / list widget\r
8676 void\r
8677 GLT_AddToList( char *name )\r
8678 {\r
8679     if( name != 0 ) {\r
8680             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8681     }\r
8682 }\r
8683 \r
8684 // low-level front-end: get line from text edit / list widget\r
8685 Boolean\r
8686 GLT_GetFromList( int index, char *name )\r
8687 {\r
8688     if( name != 0 ) {\r
8689             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8690                 return TRUE;\r
8691     }\r
8692     return FALSE;\r
8693 }\r
8694 \r
8695 void GLT_MoveSelection( HWND hDlg, int delta )\r
8696 {\r
8697     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8698     int idx2 = idx1 + delta;\r
8699     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8700 \r
8701     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8702         char buf[128];\r
8703 \r
8704         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8705         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8706         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8707         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8708     }\r
8709 }\r
8710 \r
8711 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8712 {\r
8713     switch( message )\r
8714     {\r
8715     case WM_INITDIALOG:\r
8716         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8717         \r
8718         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8719         Translate(hDlg, DLG_GameListOptions);\r
8720 \r
8721         /* Initialize list */\r
8722         GLT_TagsToList( lpUserGLT );\r
8723 \r
8724         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8725 \r
8726         break;\r
8727 \r
8728     case WM_COMMAND:\r
8729         switch( LOWORD(wParam) ) {\r
8730         case IDOK:\r
8731             GLT_ParseList();\r
8732             EndDialog( hDlg, 0 );\r
8733             return TRUE;\r
8734         case IDCANCEL:\r
8735             EndDialog( hDlg, 1 );\r
8736             return TRUE;\r
8737 \r
8738         case IDC_GLT_Default:\r
8739             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8740             return TRUE;\r
8741 \r
8742         case IDC_GLT_Restore:\r
8743             GLT_TagsToList( appData.gameListTags );\r
8744             return TRUE;\r
8745 \r
8746         case IDC_GLT_Up:\r
8747             GLT_MoveSelection( hDlg, -1 );\r
8748             return TRUE;\r
8749 \r
8750         case IDC_GLT_Down:\r
8751             GLT_MoveSelection( hDlg, +1 );\r
8752             return TRUE;\r
8753         }\r
8754 \r
8755         break;\r
8756     }\r
8757 \r
8758     return FALSE;\r
8759 }\r
8760 \r
8761 int GameListOptions()\r
8762 {\r
8763     int result;\r
8764     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8765 \r
8766       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8767 \r
8768     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8769 \r
8770     if( result == 0 ) {\r
8771         char *oldTags = appData.gameListTags;\r
8772         /* [AS] Memory leak here! */\r
8773         appData.gameListTags = strdup( lpUserGLT ); \r
8774         if(strcmp(oldTags, appData.gameListTags)) // [HGM] redo Game List when we changed something\r
8775             GameListToListBox(NULL, TRUE, ".", NULL, FALSE, FALSE); // "." as filter is kludge to select all\r
8776     }\r
8777 \r
8778     return result;\r
8779 }\r
8780 \r
8781 VOID\r
8782 DisplayIcsInteractionTitle(char *str)\r
8783 {\r
8784   char consoleTitle[MSG_SIZ];\r
8785 \r
8786     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8787     SetWindowText(hwndConsole, consoleTitle);\r
8788 \r
8789     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8790       char buf[MSG_SIZ], *p = buf, *q;\r
8791         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8792       do {\r
8793         q = strchr(p, ';');\r
8794         if(q) *q++ = 0;\r
8795         if(*p) ChatPopUp(p);\r
8796       } while(p=q);\r
8797     }\r
8798 \r
8799     SetActiveWindow(hwndMain);\r
8800 }\r
8801 \r
8802 void\r
8803 DrawPosition(int fullRedraw, Board board)\r
8804 {\r
8805   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8806 }\r
8807 \r
8808 void NotifyFrontendLogin()\r
8809 {\r
8810         if (hwndConsole)\r
8811                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8812 }\r
8813 \r
8814 VOID\r
8815 ResetFrontEnd()\r
8816 {\r
8817   fromX = fromY = -1;\r
8818   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8819     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8820     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8821     dragInfo.lastpos = dragInfo.pos;\r
8822     dragInfo.start.x = dragInfo.start.y = -1;\r
8823     dragInfo.from = dragInfo.start;\r
8824     ReleaseCapture();\r
8825     DrawPosition(TRUE, NULL);\r
8826   }\r
8827   TagsPopDown();\r
8828 }\r
8829 \r
8830 \r
8831 VOID\r
8832 CommentPopUp(char *title, char *str)\r
8833 {\r
8834   HWND hwnd = GetActiveWindow();\r
8835   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8836   SAY(str);\r
8837   SetActiveWindow(hwnd);\r
8838 }\r
8839 \r
8840 VOID\r
8841 CommentPopDown(void)\r
8842 {\r
8843   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8844   if (commentDialog) {\r
8845     ShowWindow(commentDialog, SW_HIDE);\r
8846   }\r
8847   commentUp = FALSE;\r
8848 }\r
8849 \r
8850 VOID\r
8851 EditCommentPopUp(int index, char *title, char *str)\r
8852 {\r
8853   EitherCommentPopUp(index, title, str, TRUE);\r
8854 }\r
8855 \r
8856 \r
8857 int\r
8858 Roar()\r
8859 {\r
8860   MyPlaySound(&sounds[(int)SoundRoar]);\r
8861   return 1;\r
8862 }\r
8863 \r
8864 VOID\r
8865 RingBell()\r
8866 {\r
8867   MyPlaySound(&sounds[(int)SoundMove]);\r
8868 }\r
8869 \r
8870 VOID PlayIcsWinSound()\r
8871 {\r
8872   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8873 }\r
8874 \r
8875 VOID PlayIcsLossSound()\r
8876 {\r
8877   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8878 }\r
8879 \r
8880 VOID PlayIcsDrawSound()\r
8881 {\r
8882   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8883 }\r
8884 \r
8885 VOID PlayIcsUnfinishedSound()\r
8886 {\r
8887   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8888 }\r
8889 \r
8890 VOID\r
8891 PlayAlarmSound()\r
8892 {\r
8893   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8894 }\r
8895 \r
8896 VOID\r
8897 PlayTellSound()\r
8898 {\r
8899   MyPlaySound(&textAttribs[ColorTell].sound);\r
8900 }\r
8901 \r
8902 \r
8903 VOID\r
8904 EchoOn()\r
8905 {\r
8906   HWND hInput;\r
8907   consoleEcho = TRUE;\r
8908   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8909   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8910   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8911 }\r
8912 \r
8913 \r
8914 VOID\r
8915 EchoOff()\r
8916 {\r
8917   CHARFORMAT cf;\r
8918   HWND hInput;\r
8919   consoleEcho = FALSE;\r
8920   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8921   /* This works OK: set text and background both to the same color */\r
8922   cf = consoleCF;\r
8923   cf.crTextColor = COLOR_ECHOOFF;\r
8924   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8925   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8926 }\r
8927 \r
8928 /* No Raw()...? */\r
8929 \r
8930 void Colorize(ColorClass cc, int continuation)\r
8931 {\r
8932   currentColorClass = cc;\r
8933   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8934   consoleCF.crTextColor = textAttribs[cc].color;\r
8935   consoleCF.dwEffects = textAttribs[cc].effects;\r
8936   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8937 }\r
8938 \r
8939 char *\r
8940 UserName()\r
8941 {\r
8942   static char buf[MSG_SIZ];\r
8943   DWORD bufsiz = MSG_SIZ;\r
8944 \r
8945   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8946         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8947   }\r
8948   if (!GetUserName(buf, &bufsiz)) {\r
8949     /*DisplayError("Error getting user name", GetLastError());*/\r
8950     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8951   }\r
8952   return buf;\r
8953 }\r
8954 \r
8955 char *\r
8956 HostName()\r
8957 {\r
8958   static char buf[MSG_SIZ];\r
8959   DWORD bufsiz = MSG_SIZ;\r
8960 \r
8961   if (!GetComputerName(buf, &bufsiz)) {\r
8962     /*DisplayError("Error getting host name", GetLastError());*/\r
8963     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8964   }\r
8965   return buf;\r
8966 }\r
8967 \r
8968 \r
8969 int\r
8970 ClockTimerRunning()\r
8971 {\r
8972   return clockTimerEvent != 0;\r
8973 }\r
8974 \r
8975 int\r
8976 StopClockTimer()\r
8977 {\r
8978   if (clockTimerEvent == 0) return FALSE;\r
8979   KillTimer(hwndMain, clockTimerEvent);\r
8980   clockTimerEvent = 0;\r
8981   return TRUE;\r
8982 }\r
8983 \r
8984 void\r
8985 StartClockTimer(long millisec)\r
8986 {\r
8987   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8988                              (UINT) millisec, NULL);\r
8989 }\r
8990 \r
8991 void\r
8992 DisplayWhiteClock(long timeRemaining, int highlight)\r
8993 {\r
8994   HDC hdc;\r
8995   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8996 \r
8997   if(appData.noGUI) return;\r
8998   hdc = GetDC(hwndMain);\r
8999   if (!IsIconic(hwndMain)) {\r
9000     DisplayAClock(hdc, timeRemaining, highlight, \r
9001                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
9002   }\r
9003   if (highlight && iconCurrent == iconBlack) {\r
9004     iconCurrent = iconWhite;\r
9005     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9006     if (IsIconic(hwndMain)) {\r
9007       DrawIcon(hdc, 2, 2, iconCurrent);\r
9008     }\r
9009   }\r
9010   (void) ReleaseDC(hwndMain, hdc);\r
9011   if (hwndConsole)\r
9012     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9013 }\r
9014 \r
9015 void\r
9016 DisplayBlackClock(long timeRemaining, int highlight)\r
9017 {\r
9018   HDC hdc;\r
9019   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9020 \r
9021 \r
9022   if(appData.noGUI) return;\r
9023   hdc = GetDC(hwndMain);\r
9024   if (!IsIconic(hwndMain)) {\r
9025     DisplayAClock(hdc, timeRemaining, highlight, \r
9026                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
9027   }\r
9028   if (highlight && iconCurrent == iconWhite) {\r
9029     iconCurrent = iconBlack;\r
9030     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9031     if (IsIconic(hwndMain)) {\r
9032       DrawIcon(hdc, 2, 2, iconCurrent);\r
9033     }\r
9034   }\r
9035   (void) ReleaseDC(hwndMain, hdc);\r
9036   if (hwndConsole)\r
9037     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9038 }\r
9039 \r
9040 \r
9041 int\r
9042 LoadGameTimerRunning()\r
9043 {\r
9044   return loadGameTimerEvent != 0;\r
9045 }\r
9046 \r
9047 int\r
9048 StopLoadGameTimer()\r
9049 {\r
9050   if (loadGameTimerEvent == 0) return FALSE;\r
9051   KillTimer(hwndMain, loadGameTimerEvent);\r
9052   loadGameTimerEvent = 0;\r
9053   return TRUE;\r
9054 }\r
9055 \r
9056 void\r
9057 StartLoadGameTimer(long millisec)\r
9058 {\r
9059   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9060                                 (UINT) millisec, NULL);\r
9061 }\r
9062 \r
9063 void\r
9064 AutoSaveGame()\r
9065 {\r
9066   char *defName;\r
9067   FILE *f;\r
9068   char fileTitle[MSG_SIZ];\r
9069 \r
9070   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9071   f = OpenFileDialog(hwndMain, "a", defName,\r
9072                      appData.oldSaveStyle ? "gam" : "pgn",\r
9073                      GAME_FILT, \r
9074                      _("Save Game to File"), NULL, fileTitle, NULL);\r
9075   if (f != NULL) {\r
9076     SaveGame(f, 0, "");\r
9077     fclose(f);\r
9078   }\r
9079 }\r
9080 \r
9081 \r
9082 void\r
9083 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9084 {\r
9085   if (delayedTimerEvent != 0) {\r
9086     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
9087       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9088     }\r
9089     KillTimer(hwndMain, delayedTimerEvent);\r
9090     delayedTimerEvent = 0;\r
9091     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
9092     delayedTimerCallback();\r
9093   }\r
9094   delayedTimerCallback = cb;\r
9095   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9096                                 (UINT) millisec, NULL);\r
9097 }\r
9098 \r
9099 DelayedEventCallback\r
9100 GetDelayedEvent()\r
9101 {\r
9102   if (delayedTimerEvent) {\r
9103     return delayedTimerCallback;\r
9104   } else {\r
9105     return NULL;\r
9106   }\r
9107 }\r
9108 \r
9109 void\r
9110 CancelDelayedEvent()\r
9111 {\r
9112   if (delayedTimerEvent) {\r
9113     KillTimer(hwndMain, delayedTimerEvent);\r
9114     delayedTimerEvent = 0;\r
9115   }\r
9116 }\r
9117 \r
9118 DWORD GetWin32Priority(int nice)\r
9119 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9120 /*\r
9121 REALTIME_PRIORITY_CLASS     0x00000100\r
9122 HIGH_PRIORITY_CLASS         0x00000080\r
9123 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9124 NORMAL_PRIORITY_CLASS       0x00000020\r
9125 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9126 IDLE_PRIORITY_CLASS         0x00000040\r
9127 */\r
9128         if (nice < -15) return 0x00000080;\r
9129         if (nice < 0)   return 0x00008000;\r
9130         if (nice == 0)  return 0x00000020;\r
9131         if (nice < 15)  return 0x00004000;\r
9132         return 0x00000040;\r
9133 }\r
9134 \r
9135 void RunCommand(char *cmdLine)\r
9136 {\r
9137   /* Now create the child process. */\r
9138   STARTUPINFO siStartInfo;\r
9139   PROCESS_INFORMATION piProcInfo;\r
9140 \r
9141   siStartInfo.cb = sizeof(STARTUPINFO);\r
9142   siStartInfo.lpReserved = NULL;\r
9143   siStartInfo.lpDesktop = NULL;\r
9144   siStartInfo.lpTitle = NULL;\r
9145   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9146   siStartInfo.cbReserved2 = 0;\r
9147   siStartInfo.lpReserved2 = NULL;\r
9148   siStartInfo.hStdInput = NULL;\r
9149   siStartInfo.hStdOutput = NULL;\r
9150   siStartInfo.hStdError = NULL;\r
9151 \r
9152   CreateProcess(NULL,\r
9153                 cmdLine,           /* command line */\r
9154                 NULL,      /* process security attributes */\r
9155                 NULL,      /* primary thread security attrs */\r
9156                 TRUE,      /* handles are inherited */\r
9157                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9158                 NULL,      /* use parent's environment */\r
9159                 NULL,\r
9160                 &siStartInfo, /* STARTUPINFO pointer */\r
9161                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
9162 \r
9163   CloseHandle(piProcInfo.hThread);\r
9164 }\r
9165 \r
9166 /* Start a child process running the given program.\r
9167    The process's standard output can be read from "from", and its\r
9168    standard input can be written to "to".\r
9169    Exit with fatal error if anything goes wrong.\r
9170    Returns an opaque pointer that can be used to destroy the process\r
9171    later.\r
9172 */\r
9173 int\r
9174 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9175 {\r
9176 #define BUFSIZE 4096\r
9177 \r
9178   HANDLE hChildStdinRd, hChildStdinWr,\r
9179     hChildStdoutRd, hChildStdoutWr;\r
9180   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9181   SECURITY_ATTRIBUTES saAttr;\r
9182   BOOL fSuccess;\r
9183   PROCESS_INFORMATION piProcInfo;\r
9184   STARTUPINFO siStartInfo;\r
9185   ChildProc *cp;\r
9186   char buf[MSG_SIZ];\r
9187   DWORD err;\r
9188 \r
9189   if (appData.debugMode) {\r
9190     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9191   }\r
9192 \r
9193   *pr = NoProc;\r
9194 \r
9195   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9196   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9197   saAttr.bInheritHandle = TRUE;\r
9198   saAttr.lpSecurityDescriptor = NULL;\r
9199 \r
9200   /*\r
9201    * The steps for redirecting child's STDOUT:\r
9202    *     1. Create anonymous pipe to be STDOUT for child.\r
9203    *     2. Create a noninheritable duplicate of read handle,\r
9204    *         and close the inheritable read handle.\r
9205    */\r
9206 \r
9207   /* Create a pipe for the child's STDOUT. */\r
9208   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9209     return GetLastError();\r
9210   }\r
9211 \r
9212   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9213   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9214                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9215                              FALSE,     /* not inherited */\r
9216                              DUPLICATE_SAME_ACCESS);\r
9217   if (! fSuccess) {\r
9218     return GetLastError();\r
9219   }\r
9220   CloseHandle(hChildStdoutRd);\r
9221 \r
9222   /*\r
9223    * The steps for redirecting child's STDIN:\r
9224    *     1. Create anonymous pipe to be STDIN for child.\r
9225    *     2. Create a noninheritable duplicate of write handle,\r
9226    *         and close the inheritable write handle.\r
9227    */\r
9228 \r
9229   /* Create a pipe for the child's STDIN. */\r
9230   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9231     return GetLastError();\r
9232   }\r
9233 \r
9234   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9235   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9236                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9237                              FALSE,     /* not inherited */\r
9238                              DUPLICATE_SAME_ACCESS);\r
9239   if (! fSuccess) {\r
9240     return GetLastError();\r
9241   }\r
9242   CloseHandle(hChildStdinWr);\r
9243 \r
9244   /* Arrange to (1) look in dir for the child .exe file, and\r
9245    * (2) have dir be the child's working directory.  Interpret\r
9246    * dir relative to the directory WinBoard loaded from. */\r
9247   GetCurrentDirectory(MSG_SIZ, buf);\r
9248   SetCurrentDirectory(installDir);\r
9249   SetCurrentDirectory(dir);\r
9250 \r
9251   /* Now create the child process. */\r
9252 \r
9253   siStartInfo.cb = sizeof(STARTUPINFO);\r
9254   siStartInfo.lpReserved = NULL;\r
9255   siStartInfo.lpDesktop = NULL;\r
9256   siStartInfo.lpTitle = NULL;\r
9257   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9258   siStartInfo.cbReserved2 = 0;\r
9259   siStartInfo.lpReserved2 = NULL;\r
9260   siStartInfo.hStdInput = hChildStdinRd;\r
9261   siStartInfo.hStdOutput = hChildStdoutWr;\r
9262   siStartInfo.hStdError = hChildStdoutWr;\r
9263 \r
9264   fSuccess = CreateProcess(NULL,\r
9265                            cmdLine,        /* command line */\r
9266                            NULL,           /* process security attributes */\r
9267                            NULL,           /* primary thread security attrs */\r
9268                            TRUE,           /* handles are inherited */\r
9269                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9270                            NULL,           /* use parent's environment */\r
9271                            NULL,\r
9272                            &siStartInfo, /* STARTUPINFO pointer */\r
9273                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9274 \r
9275   err = GetLastError();\r
9276   SetCurrentDirectory(buf); /* return to prev directory */\r
9277   if (! fSuccess) {\r
9278     return err;\r
9279   }\r
9280 \r
9281   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9282     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9283     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9284   }\r
9285 \r
9286   /* Close the handles we don't need in the parent */\r
9287   CloseHandle(piProcInfo.hThread);\r
9288   CloseHandle(hChildStdinRd);\r
9289   CloseHandle(hChildStdoutWr);\r
9290 \r
9291   /* Prepare return value */\r
9292   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9293   cp->kind = CPReal;\r
9294   cp->hProcess = piProcInfo.hProcess;\r
9295   cp->pid = piProcInfo.dwProcessId;\r
9296   cp->hFrom = hChildStdoutRdDup;\r
9297   cp->hTo = hChildStdinWrDup;\r
9298 \r
9299   *pr = (void *) cp;\r
9300 \r
9301   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9302      2000 where engines sometimes don't see the initial command(s)\r
9303      from WinBoard and hang.  I don't understand how that can happen,\r
9304      but the Sleep is harmless, so I've put it in.  Others have also\r
9305      reported what may be the same problem, so hopefully this will fix\r
9306      it for them too.  */\r
9307   Sleep(500);\r
9308 \r
9309   return NO_ERROR;\r
9310 }\r
9311 \r
9312 \r
9313 void\r
9314 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9315 {\r
9316   ChildProc *cp; int result;\r
9317 \r
9318   cp = (ChildProc *) pr;\r
9319   if (cp == NULL) return;\r
9320 \r
9321   switch (cp->kind) {\r
9322   case CPReal:\r
9323     /* TerminateProcess is considered harmful, so... */\r
9324     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9325     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9326     /* The following doesn't work because the chess program\r
9327        doesn't "have the same console" as WinBoard.  Maybe\r
9328        we could arrange for this even though neither WinBoard\r
9329        nor the chess program uses a console for stdio? */\r
9330     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9331 \r
9332     /* [AS] Special termination modes for misbehaving programs... */\r
9333     if( signal & 8 ) { \r
9334         result = TerminateProcess( cp->hProcess, 0 );\r
9335 \r
9336         if ( appData.debugMode) {\r
9337             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9338         }\r
9339     }\r
9340     else if( signal & 4 ) {\r
9341         DWORD dw = WaitForSingleObject( cp->hProcess, appData.delayAfterQuit*1000 + 50 ); // Wait 3 seconds at most\r
9342 \r
9343         if( dw != WAIT_OBJECT_0 ) {\r
9344             result = TerminateProcess( cp->hProcess, 0 );\r
9345 \r
9346             if ( appData.debugMode) {\r
9347                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9348             }\r
9349 \r
9350         }\r
9351     }\r
9352 \r
9353     CloseHandle(cp->hProcess);\r
9354     break;\r
9355 \r
9356   case CPComm:\r
9357     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9358     break;\r
9359 \r
9360   case CPSock:\r
9361     closesocket(cp->sock);\r
9362     WSACleanup();\r
9363     break;\r
9364 \r
9365   case CPRcmd:\r
9366     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9367     closesocket(cp->sock);\r
9368     closesocket(cp->sock2);\r
9369     WSACleanup();\r
9370     break;\r
9371   }\r
9372   free(cp);\r
9373 }\r
9374 \r
9375 void\r
9376 InterruptChildProcess(ProcRef pr)\r
9377 {\r
9378   ChildProc *cp;\r
9379 \r
9380   cp = (ChildProc *) pr;\r
9381   if (cp == NULL) return;\r
9382   switch (cp->kind) {\r
9383   case CPReal:\r
9384     /* The following doesn't work because the chess program\r
9385        doesn't "have the same console" as WinBoard.  Maybe\r
9386        we could arrange for this even though neither WinBoard\r
9387        nor the chess program uses a console for stdio */\r
9388     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9389     break;\r
9390 \r
9391   case CPComm:\r
9392   case CPSock:\r
9393     /* Can't interrupt */\r
9394     break;\r
9395 \r
9396   case CPRcmd:\r
9397     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9398     break;\r
9399   }\r
9400 }\r
9401 \r
9402 \r
9403 int\r
9404 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9405 {\r
9406   char cmdLine[MSG_SIZ];\r
9407 \r
9408   if (port[0] == NULLCHAR) {\r
9409     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9410   } else {\r
9411     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9412   }\r
9413   return StartChildProcess(cmdLine, "", pr);\r
9414 }\r
9415 \r
9416 \r
9417 /* Code to open TCP sockets */\r
9418 \r
9419 int\r
9420 OpenTCP(char *host, char *port, ProcRef *pr)\r
9421 {\r
9422   ChildProc *cp;\r
9423   int err;\r
9424   SOCKET s;\r
9425 \r
9426   struct sockaddr_in sa, mysa;\r
9427   struct hostent FAR *hp;\r
9428   unsigned short uport;\r
9429   WORD wVersionRequested;\r
9430   WSADATA wsaData;\r
9431 \r
9432   /* Initialize socket DLL */\r
9433   wVersionRequested = MAKEWORD(1, 1);\r
9434   err = WSAStartup(wVersionRequested, &wsaData);\r
9435   if (err != 0) return err;\r
9436 \r
9437   /* Make socket */\r
9438   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9439     err = WSAGetLastError();\r
9440     WSACleanup();\r
9441     return err;\r
9442   }\r
9443 \r
9444   /* Bind local address using (mostly) don't-care values.\r
9445    */\r
9446   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9447   mysa.sin_family = AF_INET;\r
9448   mysa.sin_addr.s_addr = INADDR_ANY;\r
9449   uport = (unsigned short) 0;\r
9450   mysa.sin_port = htons(uport);\r
9451   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9452       == SOCKET_ERROR) {\r
9453     err = WSAGetLastError();\r
9454     WSACleanup();\r
9455     return err;\r
9456   }\r
9457 \r
9458   /* Resolve remote host name */\r
9459   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9460   if (!(hp = gethostbyname(host))) {\r
9461     unsigned int b0, b1, b2, b3;\r
9462 \r
9463     err = WSAGetLastError();\r
9464 \r
9465     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9466       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9467       hp->h_addrtype = AF_INET;\r
9468       hp->h_length = 4;\r
9469       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9470       hp->h_addr_list[0] = (char *) malloc(4);\r
9471       hp->h_addr_list[0][0] = (char) b0;\r
9472       hp->h_addr_list[0][1] = (char) b1;\r
9473       hp->h_addr_list[0][2] = (char) b2;\r
9474       hp->h_addr_list[0][3] = (char) b3;\r
9475     } else {\r
9476       WSACleanup();\r
9477       return err;\r
9478     }\r
9479   }\r
9480   sa.sin_family = hp->h_addrtype;\r
9481   uport = (unsigned short) atoi(port);\r
9482   sa.sin_port = htons(uport);\r
9483   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9484 \r
9485   /* Make connection */\r
9486   if (connect(s, (struct sockaddr *) &sa,\r
9487               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9488     err = WSAGetLastError();\r
9489     WSACleanup();\r
9490     return err;\r
9491   }\r
9492 \r
9493   /* Prepare return value */\r
9494   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9495   cp->kind = CPSock;\r
9496   cp->sock = s;\r
9497   *pr = (ProcRef *) cp;\r
9498 \r
9499   return NO_ERROR;\r
9500 }\r
9501 \r
9502 int\r
9503 OpenCommPort(char *name, ProcRef *pr)\r
9504 {\r
9505   HANDLE h;\r
9506   COMMTIMEOUTS ct;\r
9507   ChildProc *cp;\r
9508   char fullname[MSG_SIZ];\r
9509 \r
9510   if (*name != '\\')\r
9511     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9512   else\r
9513     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9514 \r
9515   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9516                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9517   if (h == (HANDLE) -1) {\r
9518     return GetLastError();\r
9519   }\r
9520   hCommPort = h;\r
9521 \r
9522   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9523 \r
9524   /* Accumulate characters until a 100ms pause, then parse */\r
9525   ct.ReadIntervalTimeout = 100;\r
9526   ct.ReadTotalTimeoutMultiplier = 0;\r
9527   ct.ReadTotalTimeoutConstant = 0;\r
9528   ct.WriteTotalTimeoutMultiplier = 0;\r
9529   ct.WriteTotalTimeoutConstant = 0;\r
9530   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9531 \r
9532   /* Prepare return value */\r
9533   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9534   cp->kind = CPComm;\r
9535   cp->hFrom = h;\r
9536   cp->hTo = h;\r
9537   *pr = (ProcRef *) cp;\r
9538 \r
9539   return NO_ERROR;\r
9540 }\r
9541 \r
9542 int\r
9543 OpenLoopback(ProcRef *pr)\r
9544 {\r
9545   DisplayFatalError(_("Not implemented"), 0, 1);\r
9546   return NO_ERROR;\r
9547 }\r
9548 \r
9549 \r
9550 int\r
9551 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9552 {\r
9553   ChildProc *cp;\r
9554   int err;\r
9555   SOCKET s, s2, s3;\r
9556   struct sockaddr_in sa, mysa;\r
9557   struct hostent FAR *hp;\r
9558   unsigned short uport;\r
9559   WORD wVersionRequested;\r
9560   WSADATA wsaData;\r
9561   int fromPort;\r
9562   char stderrPortStr[MSG_SIZ];\r
9563 \r
9564   /* Initialize socket DLL */\r
9565   wVersionRequested = MAKEWORD(1, 1);\r
9566   err = WSAStartup(wVersionRequested, &wsaData);\r
9567   if (err != 0) return err;\r
9568 \r
9569   /* Resolve remote host name */\r
9570   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9571   if (!(hp = gethostbyname(host))) {\r
9572     unsigned int b0, b1, b2, b3;\r
9573 \r
9574     err = WSAGetLastError();\r
9575 \r
9576     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9577       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9578       hp->h_addrtype = AF_INET;\r
9579       hp->h_length = 4;\r
9580       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9581       hp->h_addr_list[0] = (char *) malloc(4);\r
9582       hp->h_addr_list[0][0] = (char) b0;\r
9583       hp->h_addr_list[0][1] = (char) b1;\r
9584       hp->h_addr_list[0][2] = (char) b2;\r
9585       hp->h_addr_list[0][3] = (char) b3;\r
9586     } else {\r
9587       WSACleanup();\r
9588       return err;\r
9589     }\r
9590   }\r
9591   sa.sin_family = hp->h_addrtype;\r
9592   uport = (unsigned short) 514;\r
9593   sa.sin_port = htons(uport);\r
9594   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9595 \r
9596   /* Bind local socket to unused "privileged" port address\r
9597    */\r
9598   s = INVALID_SOCKET;\r
9599   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9600   mysa.sin_family = AF_INET;\r
9601   mysa.sin_addr.s_addr = INADDR_ANY;\r
9602   for (fromPort = 1023;; fromPort--) {\r
9603     if (fromPort < 0) {\r
9604       WSACleanup();\r
9605       return WSAEADDRINUSE;\r
9606     }\r
9607     if (s == INVALID_SOCKET) {\r
9608       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9609         err = WSAGetLastError();\r
9610         WSACleanup();\r
9611         return err;\r
9612       }\r
9613     }\r
9614     uport = (unsigned short) fromPort;\r
9615     mysa.sin_port = htons(uport);\r
9616     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9617         == SOCKET_ERROR) {\r
9618       err = WSAGetLastError();\r
9619       if (err == WSAEADDRINUSE) continue;\r
9620       WSACleanup();\r
9621       return err;\r
9622     }\r
9623     if (connect(s, (struct sockaddr *) &sa,\r
9624       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9625       err = WSAGetLastError();\r
9626       if (err == WSAEADDRINUSE) {\r
9627         closesocket(s);\r
9628         s = -1;\r
9629         continue;\r
9630       }\r
9631       WSACleanup();\r
9632       return err;\r
9633     }\r
9634     break;\r
9635   }\r
9636 \r
9637   /* Bind stderr local socket to unused "privileged" port address\r
9638    */\r
9639   s2 = INVALID_SOCKET;\r
9640   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9641   mysa.sin_family = AF_INET;\r
9642   mysa.sin_addr.s_addr = INADDR_ANY;\r
9643   for (fromPort = 1023;; fromPort--) {\r
9644     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9645     if (fromPort < 0) {\r
9646       (void) closesocket(s);\r
9647       WSACleanup();\r
9648       return WSAEADDRINUSE;\r
9649     }\r
9650     if (s2 == INVALID_SOCKET) {\r
9651       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9652         err = WSAGetLastError();\r
9653         closesocket(s);\r
9654         WSACleanup();\r
9655         return err;\r
9656       }\r
9657     }\r
9658     uport = (unsigned short) fromPort;\r
9659     mysa.sin_port = htons(uport);\r
9660     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9661         == SOCKET_ERROR) {\r
9662       err = WSAGetLastError();\r
9663       if (err == WSAEADDRINUSE) continue;\r
9664       (void) closesocket(s);\r
9665       WSACleanup();\r
9666       return err;\r
9667     }\r
9668     if (listen(s2, 1) == SOCKET_ERROR) {\r
9669       err = WSAGetLastError();\r
9670       if (err == WSAEADDRINUSE) {\r
9671         closesocket(s2);\r
9672         s2 = INVALID_SOCKET;\r
9673         continue;\r
9674       }\r
9675       (void) closesocket(s);\r
9676       (void) closesocket(s2);\r
9677       WSACleanup();\r
9678       return err;\r
9679     }\r
9680     break;\r
9681   }\r
9682   prevStderrPort = fromPort; // remember port used\r
9683   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9684 \r
9685   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9686     err = WSAGetLastError();\r
9687     (void) closesocket(s);\r
9688     (void) closesocket(s2);\r
9689     WSACleanup();\r
9690     return err;\r
9691   }\r
9692 \r
9693   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9694     err = WSAGetLastError();\r
9695     (void) closesocket(s);\r
9696     (void) closesocket(s2);\r
9697     WSACleanup();\r
9698     return err;\r
9699   }\r
9700   if (*user == NULLCHAR) user = UserName();\r
9701   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9702     err = WSAGetLastError();\r
9703     (void) closesocket(s);\r
9704     (void) closesocket(s2);\r
9705     WSACleanup();\r
9706     return err;\r
9707   }\r
9708   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9709     err = WSAGetLastError();\r
9710     (void) closesocket(s);\r
9711     (void) closesocket(s2);\r
9712     WSACleanup();\r
9713     return err;\r
9714   }\r
9715 \r
9716   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9717     err = WSAGetLastError();\r
9718     (void) closesocket(s);\r
9719     (void) closesocket(s2);\r
9720     WSACleanup();\r
9721     return err;\r
9722   }\r
9723   (void) closesocket(s2);  /* Stop listening */\r
9724 \r
9725   /* Prepare return value */\r
9726   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9727   cp->kind = CPRcmd;\r
9728   cp->sock = s;\r
9729   cp->sock2 = s3;\r
9730   *pr = (ProcRef *) cp;\r
9731 \r
9732   return NO_ERROR;\r
9733 }\r
9734 \r
9735 \r
9736 InputSourceRef\r
9737 AddInputSource(ProcRef pr, int lineByLine,\r
9738                InputCallback func, VOIDSTAR closure)\r
9739 {\r
9740   InputSource *is, *is2 = NULL;\r
9741   ChildProc *cp = (ChildProc *) pr;\r
9742 \r
9743   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9744   is->lineByLine = lineByLine;\r
9745   is->func = func;\r
9746   is->closure = closure;\r
9747   is->second = NULL;\r
9748   is->next = is->buf;\r
9749   if (pr == NoProc) {\r
9750     is->kind = CPReal;\r
9751     consoleInputSource = is;\r
9752   } else {\r
9753     is->kind = cp->kind;\r
9754     /* \r
9755         [AS] Try to avoid a race condition if the thread is given control too early:\r
9756         we create all threads suspended so that the is->hThread variable can be\r
9757         safely assigned, then let the threads start with ResumeThread.\r
9758     */\r
9759     switch (cp->kind) {\r
9760     case CPReal:\r
9761       is->hFile = cp->hFrom;\r
9762       cp->hFrom = NULL; /* now owned by InputThread */\r
9763       is->hThread =\r
9764         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9765                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9766       break;\r
9767 \r
9768     case CPComm:\r
9769       is->hFile = cp->hFrom;\r
9770       cp->hFrom = NULL; /* now owned by InputThread */\r
9771       is->hThread =\r
9772         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9773                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9774       break;\r
9775 \r
9776     case CPSock:\r
9777       is->sock = cp->sock;\r
9778       is->hThread =\r
9779         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9780                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9781       break;\r
9782 \r
9783     case CPRcmd:\r
9784       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9785       *is2 = *is;\r
9786       is->sock = cp->sock;\r
9787       is->second = is2;\r
9788       is2->sock = cp->sock2;\r
9789       is2->second = is2;\r
9790       is->hThread =\r
9791         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9792                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9793       is2->hThread =\r
9794         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9795                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9796       break;\r
9797     }\r
9798 \r
9799     if( is->hThread != NULL ) {\r
9800         ResumeThread( is->hThread );\r
9801     }\r
9802 \r
9803     if( is2 != NULL && is2->hThread != NULL ) {\r
9804         ResumeThread( is2->hThread );\r
9805     }\r
9806   }\r
9807 \r
9808   return (InputSourceRef) is;\r
9809 }\r
9810 \r
9811 void\r
9812 RemoveInputSource(InputSourceRef isr)\r
9813 {\r
9814   InputSource *is;\r
9815 \r
9816   is = (InputSource *) isr;\r
9817   is->hThread = NULL;  /* tell thread to stop */\r
9818   CloseHandle(is->hThread);\r
9819   if (is->second != NULL) {\r
9820     is->second->hThread = NULL;\r
9821     CloseHandle(is->second->hThread);\r
9822   }\r
9823 }\r
9824 \r
9825 int no_wrap(char *message, int count)\r
9826 {\r
9827     ConsoleOutput(message, count, FALSE);\r
9828     return count;\r
9829 }\r
9830 \r
9831 int\r
9832 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9833 {\r
9834   DWORD dOutCount;\r
9835   int outCount = SOCKET_ERROR;\r
9836   ChildProc *cp = (ChildProc *) pr;\r
9837   static OVERLAPPED ovl;\r
9838 \r
9839   static int line = 0;\r
9840 \r
9841   if (pr == NoProc)\r
9842   {\r
9843     if (appData.noJoin || !appData.useInternalWrap)\r
9844       return no_wrap(message, count);\r
9845     else\r
9846     {\r
9847       int width = get_term_width();\r
9848       int len = wrap(NULL, message, count, width, &line);\r
9849       char *msg = malloc(len);\r
9850       int dbgchk;\r
9851 \r
9852       if (!msg)\r
9853         return no_wrap(message, count);\r
9854       else\r
9855       {\r
9856         dbgchk = wrap(msg, message, count, width, &line);\r
9857         if (dbgchk != len && appData.debugMode)\r
9858             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9859         ConsoleOutput(msg, len, FALSE);\r
9860         free(msg);\r
9861         return len;\r
9862       }\r
9863     }\r
9864   }\r
9865 \r
9866   if (ovl.hEvent == NULL) {\r
9867     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9868   }\r
9869   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9870 \r
9871   switch (cp->kind) {\r
9872   case CPSock:\r
9873   case CPRcmd:\r
9874     outCount = send(cp->sock, message, count, 0);\r
9875     if (outCount == SOCKET_ERROR) {\r
9876       *outError = WSAGetLastError();\r
9877     } else {\r
9878       *outError = NO_ERROR;\r
9879     }\r
9880     break;\r
9881 \r
9882   case CPReal:\r
9883     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9884                   &dOutCount, NULL)) {\r
9885       *outError = NO_ERROR;\r
9886       outCount = (int) dOutCount;\r
9887     } else {\r
9888       *outError = GetLastError();\r
9889     }\r
9890     break;\r
9891 \r
9892   case CPComm:\r
9893     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9894                             &dOutCount, &ovl);\r
9895     if (*outError == NO_ERROR) {\r
9896       outCount = (int) dOutCount;\r
9897     }\r
9898     break;\r
9899   }\r
9900   return outCount;\r
9901 }\r
9902 \r
9903 void\r
9904 DoSleep(int n)\r
9905 {\r
9906     if(n != 0) Sleep(n);\r
9907 }\r
9908 \r
9909 int\r
9910 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9911                        long msdelay)\r
9912 {\r
9913   /* Ignore delay, not implemented for WinBoard */\r
9914   return OutputToProcess(pr, message, count, outError);\r
9915 }\r
9916 \r
9917 \r
9918 void\r
9919 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9920                         char *buf, int count, int error)\r
9921 {\r
9922   DisplayFatalError(_("Not implemented"), 0, 1);\r
9923 }\r
9924 \r
9925 /* see wgamelist.c for Game List functions */\r
9926 /* see wedittags.c for Edit Tags functions */\r
9927 \r
9928 \r
9929 int\r
9930 ICSInitScript()\r
9931 {\r
9932   FILE *f;\r
9933   char buf[MSG_SIZ];\r
9934   char *dummy;\r
9935 \r
9936   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9937     f = fopen(buf, "r");\r
9938     if (f != NULL) {\r
9939       ProcessICSInitScript(f);\r
9940       fclose(f);\r
9941       return TRUE;\r
9942     }\r
9943   }\r
9944   return FALSE;\r
9945 }\r
9946 \r
9947 \r
9948 VOID\r
9949 StartAnalysisClock()\r
9950 {\r
9951   if (analysisTimerEvent) return;\r
9952   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9953                                         (UINT) 2000, NULL);\r
9954 }\r
9955 \r
9956 VOID\r
9957 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9958 {\r
9959   highlightInfo.sq[0].x = fromX;\r
9960   highlightInfo.sq[0].y = fromY;\r
9961   highlightInfo.sq[1].x = toX;\r
9962   highlightInfo.sq[1].y = toY;\r
9963 }\r
9964 \r
9965 VOID\r
9966 ClearHighlights()\r
9967 {\r
9968   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9969     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9970 }\r
9971 \r
9972 VOID\r
9973 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9974 {\r
9975   premoveHighlightInfo.sq[0].x = fromX;\r
9976   premoveHighlightInfo.sq[0].y = fromY;\r
9977   premoveHighlightInfo.sq[1].x = toX;\r
9978   premoveHighlightInfo.sq[1].y = toY;\r
9979 }\r
9980 \r
9981 VOID\r
9982 ClearPremoveHighlights()\r
9983 {\r
9984   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9985     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9986 }\r
9987 \r
9988 VOID\r
9989 ShutDownFrontEnd()\r
9990 {\r
9991   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9992   DeleteClipboardTempFiles();\r
9993 }\r
9994 \r
9995 void\r
9996 BoardToTop()\r
9997 {\r
9998     if (IsIconic(hwndMain))\r
9999       ShowWindow(hwndMain, SW_RESTORE);\r
10000 \r
10001     SetActiveWindow(hwndMain);\r
10002 }\r
10003 \r
10004 /*\r
10005  * Prototypes for animation support routines\r
10006  */\r
10007 static void ScreenSquare(int column, int row, POINT * pt);\r
10008 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10009      POINT frames[], int * nFrames);\r
10010 \r
10011 \r
10012 #define kFactor 4\r
10013 \r
10014 void\r
10015 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
10016 {       // [HGM] atomic: animate blast wave\r
10017         int i;\r
10018 \r
10019         explodeInfo.fromX = fromX;\r
10020         explodeInfo.fromY = fromY;\r
10021         explodeInfo.toX = toX;\r
10022         explodeInfo.toY = toY;\r
10023         for(i=1; i<4*kFactor; i++) {\r
10024             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
10025             DrawPosition(FALSE, board);\r
10026             Sleep(appData.animSpeed);\r
10027         }\r
10028         explodeInfo.radius = 0;\r
10029         DrawPosition(TRUE, board);\r
10030 }\r
10031 \r
10032 void\r
10033 AnimateMove(board, fromX, fromY, toX, toY)\r
10034      Board board;\r
10035      int fromX;\r
10036      int fromY;\r
10037      int toX;\r
10038      int toY;\r
10039 {\r
10040   ChessSquare piece;\r
10041   int x = toX, y = toY;\r
10042   POINT start, finish, mid;\r
10043   POINT frames[kFactor * 2 + 1];\r
10044   int nFrames, n;\r
10045 \r
10046   if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();\r
10047 \r
10048   if (!appData.animate) return;\r
10049   if (doingSizing) return;\r
10050   if (fromY < 0 || fromX < 0) return;\r
10051   piece = board[fromY][fromX];\r
10052   if (piece >= EmptySquare) return;\r
10053 \r
10054   if(killX >= 0) toX = killX, toY = killY; // [HGM] lion: first to kill square\r
10055 \r
10056 again:\r
10057 \r
10058   ScreenSquare(fromX, fromY, &start);\r
10059   ScreenSquare(toX, toY, &finish);\r
10060 \r
10061   /* All moves except knight jumps move in straight line */\r
10062   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
10063     mid.x = start.x + (finish.x - start.x) / 2;\r
10064     mid.y = start.y + (finish.y - start.y) / 2;\r
10065   } else {\r
10066     /* Knight: make straight movement then diagonal */\r
10067     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10068        mid.x = start.x + (finish.x - start.x) / 2;\r
10069        mid.y = start.y;\r
10070      } else {\r
10071        mid.x = start.x;\r
10072        mid.y = start.y + (finish.y - start.y) / 2;\r
10073      }\r
10074   }\r
10075   \r
10076   /* Don't use as many frames for very short moves */\r
10077   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10078     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10079   else\r
10080     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10081 \r
10082   animInfo.from.x = fromX;\r
10083   animInfo.from.y = fromY;\r
10084   animInfo.to.x = toX;\r
10085   animInfo.to.y = toY;\r
10086   animInfo.lastpos = start;\r
10087   animInfo.piece = piece;\r
10088   for (n = 0; n < nFrames; n++) {\r
10089     animInfo.pos = frames[n];\r
10090     DrawPosition(FALSE, NULL);\r
10091     animInfo.lastpos = animInfo.pos;\r
10092     Sleep(appData.animSpeed);\r
10093   }\r
10094   animInfo.pos = finish;\r
10095   DrawPosition(FALSE, NULL);\r
10096 \r
10097   if(toX != x || toY != y) { fromX = toX; fromY = toY; toX = x; toY = y; goto again; } // second leg\r
10098 \r
10099   animInfo.piece = EmptySquare;\r
10100   Explode(board, fromX, fromY, toX, toY);\r
10101 }\r
10102 \r
10103 /*      Convert board position to corner of screen rect and color       */\r
10104 \r
10105 static void\r
10106 ScreenSquare(column, row, pt)\r
10107      int column; int row; POINT * pt;\r
10108 {\r
10109   if (flipView) {\r
10110     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
10111     pt->y = lineGap + row * (squareSize + lineGap) + border;\r
10112   } else {\r
10113     pt->x = lineGap + column * (squareSize + lineGap) + border;\r
10114     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
10115   }\r
10116 }\r
10117 \r
10118 /*      Generate a series of frame coords from start->mid->finish.\r
10119         The movement rate doubles until the half way point is\r
10120         reached, then halves back down to the final destination,\r
10121         which gives a nice slow in/out effect. The algorithmn\r
10122         may seem to generate too many intermediates for short\r
10123         moves, but remember that the purpose is to attract the\r
10124         viewers attention to the piece about to be moved and\r
10125         then to where it ends up. Too few frames would be less\r
10126         noticeable.                                             */\r
10127 \r
10128 static void\r
10129 Tween(start, mid, finish, factor, frames, nFrames)\r
10130      POINT * start; POINT * mid;\r
10131      POINT * finish; int factor;\r
10132      POINT frames[]; int * nFrames;\r
10133 {\r
10134   int n, fraction = 1, count = 0;\r
10135 \r
10136   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10137   for (n = 0; n < factor; n++)\r
10138     fraction *= 2;\r
10139   for (n = 0; n < factor; n++) {\r
10140     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10141     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10142     count ++;\r
10143     fraction = fraction / 2;\r
10144   }\r
10145   \r
10146   /* Midpoint */\r
10147   frames[count] = *mid;\r
10148   count ++;\r
10149   \r
10150   /* Slow out, stepping 1/2, then 1/4, ... */\r
10151   fraction = 2;\r
10152   for (n = 0; n < factor; n++) {\r
10153     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10154     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10155     count ++;\r
10156     fraction = fraction * 2;\r
10157   }\r
10158   *nFrames = count;\r
10159 }\r
10160 \r
10161 void\r
10162 SettingsPopUp(ChessProgramState *cps)\r
10163 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
10164       EngineOptionsPopup(savedHwnd, cps);\r
10165 }\r
10166 \r
10167 int flock(int fid, int code)\r
10168 {\r
10169     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
10170     OVERLAPPED ov;\r
10171     ov.hEvent = NULL;\r
10172     ov.Offset = 0;\r
10173     ov.OffsetHigh = 0;\r
10174     switch(code) {\r
10175       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
10176 \r
10177       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
10178       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
10179       default: return -1;\r
10180     }\r
10181     return 0;\r
10182 }\r
10183 \r
10184 char *\r
10185 Col2Text (int n)\r
10186 {\r
10187     static int i=0;\r
10188     static char col[8][20];\r
10189     COLORREF color = *(COLORREF *) colorVariable[n];\r
10190     i = i+1 & 7;\r
10191     snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
10192     return col[i];\r
10193 }\r
10194 \r
10195 void\r
10196 ActivateTheme (int new)\r
10197 {   // Redo initialization of features depending on options that can occur in themes\r
10198    InitTextures();\r
10199    if(new) InitDrawingColors();\r
10200    fontBitmapSquareSize = 0; // request creation of new font pieces\r
10201    InitDrawingSizes(boardSize, 0);\r
10202    InvalidateRect(hwndMain, NULL, TRUE);\r
10203 }\r