2301293b7754d6d4537ab28e0fb6d2cae956d6f1
[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 \r
3757 \r
3758 \r
3759 VOID\r
3760 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3761 {\r
3762   static Board lastReq[2], lastDrawn[2];\r
3763   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3764   static int lastDrawnFlipView = 0;\r
3765   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3766   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3767   HDC tmphdc;\r
3768   HDC hdcmem;\r
3769   HBITMAP bufferBitmap;\r
3770   HBITMAP oldBitmap;\r
3771   RECT Rect;\r
3772   HRGN clips[MAX_CLIPS];\r
3773   ChessSquare dragged_piece = EmptySquare;\r
3774   int nr = twoBoards*partnerUp;\r
3775 \r
3776   /* I'm undecided on this - this function figures out whether a full\r
3777    * repaint is necessary on its own, so there's no real reason to have the\r
3778    * caller tell it that.  I think this can safely be set to FALSE - but\r
3779    * if we trust the callers not to request full repaints unnessesarily, then\r
3780    * we could skip some clipping work.  In other words, only request a full\r
3781    * redraw when the majority of pieces have changed positions (ie. flip, \r
3782    * gamestart and similar)  --Hawk\r
3783    */\r
3784   Boolean fullrepaint = repaint;\r
3785 \r
3786   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3787 \r
3788   if( DrawPositionNeedsFullRepaint() ) {\r
3789       fullrepaint = TRUE;\r
3790   }\r
3791 \r
3792   if (board == NULL) {\r
3793     if (!lastReqValid[nr]) {\r
3794       return;\r
3795     }\r
3796     board = lastReq[nr];\r
3797   } else {\r
3798     CopyBoard(lastReq[nr], board);\r
3799     lastReqValid[nr] = 1;\r
3800   }\r
3801 \r
3802   if (doingSizing) {\r
3803     return;\r
3804   }\r
3805 \r
3806   if (IsIconic(hwndMain)) {\r
3807     return;\r
3808   }\r
3809 \r
3810   if (hdc == NULL) {\r
3811     hdc = GetDC(hwndMain);\r
3812     if (!appData.monoMode) {\r
3813       SelectPalette(hdc, hPal, FALSE);\r
3814       RealizePalette(hdc);\r
3815     }\r
3816     releaseDC = TRUE;\r
3817   } else {\r
3818     releaseDC = FALSE;\r
3819   }\r
3820 \r
3821   /* Create some work-DCs */\r
3822   hdcmem = CreateCompatibleDC(hdc);\r
3823   tmphdc = CreateCompatibleDC(hdc);\r
3824 \r
3825   /* If dragging is in progress, we temporarely remove the piece */\r
3826   /* [HGM] or temporarily decrease count if stacked              */\r
3827   /*       !! Moved to before board compare !!                   */\r
3828   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3829     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3830     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3831             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3832         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3833     } else \r
3834     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3835             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3836         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3837     } else \r
3838         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3839   }\r
3840 \r
3841   /* Figure out which squares need updating by comparing the \r
3842    * newest board with the last drawn board and checking if\r
3843    * flipping has changed.\r
3844    */\r
3845   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3846     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3847       for (column = 0; column < BOARD_WIDTH; column++) {\r
3848         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3849           SquareToPos(row, column, &x, &y);\r
3850           clips[num_clips++] =\r
3851             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3852         }\r
3853       }\r
3854     }\r
3855    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3856     for (i=0; i<2; i++) {\r
3857       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3858           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3859         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3860             lastDrawnHighlight.sq[i].y >= 0) {\r
3861           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3862                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3863           clips[num_clips++] =\r
3864             CreateRectRgn(x - lineGap, y - lineGap, \r
3865                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3866         }\r
3867         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3868           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3869           clips[num_clips++] =\r
3870             CreateRectRgn(x - lineGap, y - lineGap, \r
3871                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3872         }\r
3873       }\r
3874     }\r
3875     for (i=0; i<2; i++) {\r
3876       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3877           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3878         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3879             lastDrawnPremove.sq[i].y >= 0) {\r
3880           SquareToPos(lastDrawnPremove.sq[i].y,\r
3881                       lastDrawnPremove.sq[i].x, &x, &y);\r
3882           clips[num_clips++] =\r
3883             CreateRectRgn(x - lineGap, y - lineGap, \r
3884                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3885         }\r
3886         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3887             premoveHighlightInfo.sq[i].y >= 0) {\r
3888           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3889                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3890           clips[num_clips++] =\r
3891             CreateRectRgn(x - lineGap, y - lineGap, \r
3892                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3893         }\r
3894       }\r
3895     }\r
3896    } else { // nr == 1\r
3897         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3898         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3899         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3900         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3901       for (i=0; i<2; i++) {\r
3902         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3903             partnerHighlightInfo.sq[i].y >= 0) {\r
3904           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3905                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3906           clips[num_clips++] =\r
3907             CreateRectRgn(x - lineGap, y - lineGap, \r
3908                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3909         }\r
3910         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3911             oldPartnerHighlight.sq[i].y >= 0) {\r
3912           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3913                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3914           clips[num_clips++] =\r
3915             CreateRectRgn(x - lineGap, y - lineGap, \r
3916                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3917         }\r
3918       }\r
3919    }\r
3920   } else {\r
3921     fullrepaint = TRUE;\r
3922   }\r
3923 \r
3924   /* Create a buffer bitmap - this is the actual bitmap\r
3925    * being written to.  When all the work is done, we can\r
3926    * copy it to the real DC (the screen).  This avoids\r
3927    * the problems with flickering.\r
3928    */\r
3929   GetClientRect(hwndMain, &Rect);\r
3930   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3931                                         Rect.bottom-Rect.top+1);\r
3932   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3933   if (!appData.monoMode) {\r
3934     SelectPalette(hdcmem, hPal, FALSE);\r
3935   }\r
3936 \r
3937   /* Create clips for dragging */\r
3938   if (!fullrepaint) {\r
3939     if (dragInfo.from.x >= 0) {\r
3940       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3941       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3942     }\r
3943     if (dragInfo.start.x >= 0) {\r
3944       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3945       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3946     }\r
3947     if (dragInfo.pos.x >= 0) {\r
3948       x = dragInfo.pos.x - squareSize / 2;\r
3949       y = dragInfo.pos.y - squareSize / 2;\r
3950       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3951     }\r
3952     if (dragInfo.lastpos.x >= 0) {\r
3953       x = dragInfo.lastpos.x - squareSize / 2;\r
3954       y = dragInfo.lastpos.y - squareSize / 2;\r
3955       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3956     }\r
3957   }\r
3958 \r
3959   /* Are we animating a move?  \r
3960    * If so, \r
3961    *   - remove the piece from the board (temporarely)\r
3962    *   - calculate the clipping region\r
3963    */\r
3964   if (!fullrepaint) {\r
3965     if (animInfo.piece != EmptySquare) {\r
3966       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3967       x = boardRect.left + animInfo.lastpos.x;\r
3968       y = boardRect.top + animInfo.lastpos.y;\r
3969       x2 = boardRect.left + animInfo.pos.x;\r
3970       y2 = boardRect.top + animInfo.pos.y;\r
3971       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3972       /* Slight kludge.  The real problem is that after AnimateMove is\r
3973          done, the position on the screen does not match lastDrawn.\r
3974          This currently causes trouble only on e.p. captures in\r
3975          atomic, where the piece moves to an empty square and then\r
3976          explodes.  The old and new positions both had an empty square\r
3977          at the destination, but animation has drawn a piece there and\r
3978          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3979 \r
3980       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3981     }\r
3982   }\r
3983 \r
3984   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3985   if (num_clips == 0)\r
3986     fullrepaint = TRUE;\r
3987 \r
3988   /* Set clipping on the memory DC */\r
3989   if (!fullrepaint) {\r
3990     SelectClipRgn(hdcmem, clips[0]);\r
3991     for (x = 1; x < num_clips; x++) {\r
3992       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3993         abort();  // this should never ever happen!\r
3994     }\r
3995   }\r
3996 \r
3997   /* Do all the drawing to the memory DC */\r
3998   if(explodeInfo.radius) { // [HGM] atomic\r
3999         HBRUSH oldBrush;\r
4000         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
4001         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
4002         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
4003         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
4004         x += squareSize/2;\r
4005         y += squareSize/2;\r
4006         if(!fullrepaint) {\r
4007           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4008           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4009         }\r
4010         DrawGridOnDC(hdcmem);\r
4011         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
4012         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
4013         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4014         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
4015         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4016         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4017         SelectObject(hdcmem, oldBrush);\r
4018   } else {\r
4019     if(border) DrawBackgroundOnDC(hdcmem);\r
4020     DrawGridOnDC(hdcmem);\r
4021     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
4022         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
4023         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
4024     } else {\r
4025         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
4026         oldPartnerHighlight = partnerHighlightInfo;\r
4027     }\r
4028     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4029   }\r
4030   if(nr == 0) // [HGM] dual: markers only on left board\r
4031   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4032     for (column = 0; column < BOARD_WIDTH; column++) {\r
4033         if (marker[row][column]) { // marker changes only occur with full repaint!\r
4034             HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);\r
4035             SquareToPos(row, column, &x, &y);\r
4036             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
4037                           x + 3*squareSize/4, y + 3*squareSize/4);\r
4038             SelectObject(hdcmem, oldBrush);\r
4039         }\r
4040     }\r
4041   }\r
4042 \r
4043   if( appData.highlightMoveWithArrow ) {\r
4044 \r
4045     DrawArrowHighlight(hdcmem);\r
4046   }\r
4047 \r
4048   DrawCoordsOnDC(hdcmem);\r
4049 \r
4050   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
4051                  /* to make sure lastDrawn contains what is actually drawn */\r
4052 \r
4053   /* Put the dragged piece back into place and draw it (out of place!) */\r
4054     if (dragged_piece != EmptySquare) {\r
4055     /* [HGM] or restack */\r
4056     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4057                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4058     else\r
4059     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4060                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4061 \r
4062     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4063     x = dragInfo.pos.x - squareSize / 2;\r
4064     y = dragInfo.pos.y - squareSize / 2;\r
4065     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
4066                   ((int) dragInfo.piece < (int) BlackPawn), \r
4067                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4068   }   \r
4069   \r
4070   /* Put the animated piece back into place and draw it */\r
4071   if (animInfo.piece != EmptySquare) {\r
4072     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4073     x = boardRect.left + animInfo.pos.x;\r
4074     y = boardRect.top + animInfo.pos.y;\r
4075     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4076                   ((int) animInfo.piece < (int) BlackPawn),\r
4077                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4078   }\r
4079 \r
4080   /* Release the bufferBitmap by selecting in the old bitmap \r
4081    * and delete the memory DC\r
4082    */\r
4083   SelectObject(hdcmem, oldBitmap);\r
4084   DeleteDC(hdcmem);\r
4085 \r
4086   /* Set clipping on the target DC */\r
4087   if (!fullrepaint) {\r
4088     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
4089         RECT rect;\r
4090         GetRgnBox(clips[x], &rect);\r
4091         DeleteObject(clips[x]);\r
4092         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
4093                           rect.right + wpMain.width/2, rect.bottom);\r
4094     }\r
4095     SelectClipRgn(hdc, clips[0]);\r
4096     for (x = 1; x < num_clips; x++) {\r
4097       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4098         abort();   // this should never ever happen!\r
4099     } \r
4100   }\r
4101 \r
4102   /* Copy the new bitmap onto the screen in one go.\r
4103    * This way we avoid any flickering\r
4104    */\r
4105   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4106   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
4107          boardRect.right - boardRect.left,\r
4108          boardRect.bottom - boardRect.top,\r
4109          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4110   if(saveDiagFlag) { \r
4111     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
4112     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4113     HBITMAP src = bufferBitmap, obmp; HDC tmp = CreateCompatibleDC(hdc);\r
4114 \r
4115     bufferBitmap = CreateCompatibleBitmap(hdc, boardRect.right-boardRect.left, Rect.bottom-Rect.top-2*OUTER_MARGIN);\r
4116     obmp = SelectObject(tmp, bufferBitmap);\r
4117     BitBlt(tmp, 0, 0, boardRect.right - boardRect.left, Rect.bottom - Rect.top - 2*OUTER_MARGIN,\r
4118            tmphdc, boardRect.left, OUTER_MARGIN, SRCCOPY);\r
4119     GetObject(bufferBitmap, sizeof(b), &b);\r
4120     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
4121         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4122         bih.biWidth = b.bmWidth;\r
4123         bih.biHeight = b.bmHeight;\r
4124         bih.biPlanes = 1;\r
4125         bih.biBitCount = b.bmBitsPixel;\r
4126         bih.biCompression = 0;\r
4127         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4128         bih.biXPelsPerMeter = 0;\r
4129         bih.biYPelsPerMeter = 0;\r
4130         bih.biClrUsed = 0;\r
4131         bih.biClrImportant = 0;\r
4132 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4133 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4134         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4135 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4136 \r
4137         wb = b.bmWidthBytes;\r
4138         // count colors\r
4139         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4140                 int k = ((int*) pData)[i];\r
4141                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4142                 if(j >= 16) break;\r
4143                 color[j] = k;\r
4144                 if(j >= nrColors) nrColors = j+1;\r
4145         }\r
4146         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4147                 INT p = 0;\r
4148                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4149                     for(w=0; w<(wb>>2); w+=2) {\r
4150                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4151                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4152                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4153                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4154                         pData[p++] = m | j<<4;\r
4155                     }\r
4156                     while(p&3) pData[p++] = 0;\r
4157                 }\r
4158                 fac = 3;\r
4159                 wb = ((wb+31)>>5)<<2;\r
4160         }\r
4161         // write BITMAPFILEHEADER\r
4162         fprintf(diagFile, "BM");\r
4163         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4164         fputDW(diagFile, 0);\r
4165         fputDW(diagFile, 0x36 + (fac?64:0));\r
4166         // write BITMAPINFOHEADER\r
4167         fputDW(diagFile, 40);\r
4168         fputDW(diagFile, b.bmWidth);\r
4169         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4170         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4171         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4172         fputDW(diagFile, 0);\r
4173         fputDW(diagFile, 0);\r
4174         fputDW(diagFile, 0);\r
4175         fputDW(diagFile, 0);\r
4176         fputDW(diagFile, 0);\r
4177         fputDW(diagFile, 0);\r
4178         // write color table\r
4179         if(fac)\r
4180         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4181         // write bitmap data\r
4182         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4183                 fputc(pData[i], diagFile);\r
4184         free(pData);\r
4185      }\r
4186      DeleteObject(bufferBitmap); bufferBitmap = src;\r
4187      SelectObject(tmp, obmp);\r
4188      DeleteDC(tmp);\r
4189   }\r
4190 \r
4191   SelectObject(tmphdc, oldBitmap);\r
4192 \r
4193   /* Massive cleanup */\r
4194   for (x = 0; x < num_clips; x++)\r
4195     DeleteObject(clips[x]);\r
4196 \r
4197   DeleteDC(tmphdc);\r
4198   DeleteObject(bufferBitmap);\r
4199 \r
4200   if (releaseDC) \r
4201     ReleaseDC(hwndMain, hdc);\r
4202   \r
4203   if (lastDrawnFlipView != flipView && nr == 0) {\r
4204     if (flipView)\r
4205       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4206     else\r
4207       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4208   }\r
4209 \r
4210 /*  CopyBoard(lastDrawn, board);*/\r
4211   lastDrawnHighlight = highlightInfo;\r
4212   lastDrawnPremove   = premoveHighlightInfo;\r
4213   lastDrawnFlipView = flipView;\r
4214   lastDrawnValid[nr] = 1;\r
4215 }\r
4216 \r
4217 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4218 int\r
4219 SaveDiagram(f)\r
4220      FILE *f;\r
4221 {\r
4222     saveDiagFlag = 1; diagFile = f;\r
4223     HDCDrawPosition(NULL, TRUE, NULL);\r
4224     saveDiagFlag = 0;\r
4225 \r
4226     fclose(f);\r
4227     return TRUE;\r
4228 }\r
4229 \r
4230 \r
4231 /*---------------------------------------------------------------------------*\\r
4232 | CLIENT PAINT PROCEDURE\r
4233 |   This is the main event-handler for the WM_PAINT message.\r
4234 |\r
4235 \*---------------------------------------------------------------------------*/\r
4236 VOID\r
4237 PaintProc(HWND hwnd)\r
4238 {\r
4239   HDC         hdc;\r
4240   PAINTSTRUCT ps;\r
4241   HFONT       oldFont;\r
4242 \r
4243   if((hdc = BeginPaint(hwnd, &ps))) {\r
4244     if (IsIconic(hwnd)) {\r
4245       DrawIcon(hdc, 2, 2, iconCurrent);\r
4246     } else {\r
4247       if (!appData.monoMode) {\r
4248         SelectPalette(hdc, hPal, FALSE);\r
4249         RealizePalette(hdc);\r
4250       }\r
4251       HDCDrawPosition(hdc, 1, NULL);\r
4252       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4253         flipView = !flipView; partnerUp = !partnerUp;\r
4254         HDCDrawPosition(hdc, 1, NULL);\r
4255         flipView = !flipView; partnerUp = !partnerUp;\r
4256       }\r
4257       oldFont =\r
4258         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4259       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4260                  ETO_CLIPPED|ETO_OPAQUE,\r
4261                  &messageRect, messageText, strlen(messageText), NULL);\r
4262       SelectObject(hdc, oldFont);\r
4263       DisplayBothClocks();\r
4264       DisplayLogos();\r
4265     }\r
4266     EndPaint(hwnd,&ps);\r
4267   }\r
4268 \r
4269   return;\r
4270 }\r
4271 \r
4272 \r
4273 /*\r
4274  * If the user selects on a border boundary, return -1; if off the board,\r
4275  *   return -2.  Otherwise map the event coordinate to the square.\r
4276  * The offset boardRect.left or boardRect.top must already have been\r
4277  *   subtracted from x.\r
4278  */\r
4279 int EventToSquare(x, limit)\r
4280      int x, limit;\r
4281 {\r
4282   if (x <= border)\r
4283     return -2;\r
4284   if (x < lineGap + border)\r
4285     return -1;\r
4286   x -= lineGap + border;\r
4287   if ((x % (squareSize + lineGap)) >= squareSize)\r
4288     return -1;\r
4289   x /= (squareSize + lineGap);\r
4290     if (x >= limit)\r
4291     return -2;\r
4292   return x;\r
4293 }\r
4294 \r
4295 typedef struct {\r
4296   char piece;\r
4297   int command;\r
4298   char* name;\r
4299 } DropEnable;\r
4300 \r
4301 DropEnable dropEnables[] = {\r
4302   { 'P', DP_Pawn, N_("Pawn") },\r
4303   { 'N', DP_Knight, N_("Knight") },\r
4304   { 'B', DP_Bishop, N_("Bishop") },\r
4305   { 'R', DP_Rook, N_("Rook") },\r
4306   { 'Q', DP_Queen, N_("Queen") },\r
4307 };\r
4308 \r
4309 VOID\r
4310 SetupDropMenu(HMENU hmenu)\r
4311 {\r
4312   int i, count, enable;\r
4313   char *p;\r
4314   extern char white_holding[], black_holding[];\r
4315   char item[MSG_SIZ];\r
4316 \r
4317   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4318     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4319                dropEnables[i].piece);\r
4320     count = 0;\r
4321     while (p && *p++ == dropEnables[i].piece) count++;\r
4322       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4323     enable = count > 0 || !appData.testLegality\r
4324       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4325                       && !appData.icsActive);\r
4326     ModifyMenu(hmenu, dropEnables[i].command,\r
4327                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4328                dropEnables[i].command, item);\r
4329   }\r
4330 }\r
4331 \r
4332 void DragPieceBegin(int x, int y, Boolean instantly)\r
4333 {\r
4334       dragInfo.lastpos.x = boardRect.left + x;\r
4335       dragInfo.lastpos.y = boardRect.top + y;\r
4336       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4337       dragInfo.from.x = fromX;\r
4338       dragInfo.from.y = fromY;\r
4339       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4340       dragInfo.start = dragInfo.from;\r
4341       SetCapture(hwndMain);\r
4342 }\r
4343 \r
4344 void DragPieceEnd(int x, int y)\r
4345 {\r
4346     ReleaseCapture();\r
4347     dragInfo.start.x = dragInfo.start.y = -1;\r
4348     dragInfo.from = dragInfo.start;\r
4349     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4350 }\r
4351 \r
4352 void ChangeDragPiece(ChessSquare piece)\r
4353 {\r
4354     dragInfo.piece = piece;\r
4355 }\r
4356 \r
4357 /* Event handler for mouse messages */\r
4358 VOID\r
4359 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4360 {\r
4361   int x, y, menuNr;\r
4362   POINT pt;\r
4363   static int recursive = 0;\r
4364   HMENU hmenu;\r
4365   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4366 \r
4367   if (recursive) {\r
4368     if (message == WM_MBUTTONUP) {\r
4369       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4370          to the middle button: we simulate pressing the left button too!\r
4371          */\r
4372       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4373       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4374     }\r
4375     return;\r
4376   }\r
4377   recursive++;\r
4378   \r
4379   pt.x = LOWORD(lParam);\r
4380   pt.y = HIWORD(lParam);\r
4381   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4382   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4383   if (!flipView && y >= 0) {\r
4384     y = BOARD_HEIGHT - 1 - y;\r
4385   }\r
4386   if (flipView && x >= 0) {\r
4387     x = BOARD_WIDTH - 1 - x;\r
4388   }\r
4389 \r
4390   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4391   controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status\r
4392 \r
4393   switch (message) {\r
4394   case WM_LBUTTONDOWN:\r
4395       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4396         ClockClick(flipClock); break;\r
4397       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4398         ClockClick(!flipClock); break;\r
4399       }\r
4400     if(dragging) { // [HGM] lion: don't destroy dragging info if we are already dragging\r
4401       dragInfo.start.x = dragInfo.start.y = -1;\r
4402       dragInfo.from = dragInfo.start;\r
4403     }\r
4404     if(fromX == -1 && frozen) { // not sure where this is for\r
4405                 fromX = fromY = -1; \r
4406       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4407       break;\r
4408     }\r
4409       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4410       DrawPosition(TRUE, NULL);\r
4411     break;\r
4412 \r
4413   case WM_LBUTTONUP:\r
4414       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4415       DrawPosition(TRUE, NULL);\r
4416     break;\r
4417 \r
4418   case WM_MOUSEMOVE:\r
4419     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4420     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4421     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4422     if ((appData.animateDragging || appData.highlightDragging)\r
4423         && (wParam & MK_LBUTTON || dragging == 2)\r
4424         && dragInfo.from.x >= 0) \r
4425     {\r
4426       BOOL full_repaint = FALSE;\r
4427 \r
4428       if (appData.animateDragging) {\r
4429         dragInfo.pos = pt;\r
4430       }\r
4431       if (appData.highlightDragging) {\r
4432         HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);\r
4433         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4434             full_repaint = TRUE;\r
4435         }\r
4436       }\r
4437       \r
4438       DrawPosition( full_repaint, NULL);\r
4439       \r
4440       dragInfo.lastpos = dragInfo.pos;\r
4441     }\r
4442     break;\r
4443 \r
4444   case WM_MOUSEWHEEL: // [DM]\r
4445     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4446        /* Mouse Wheel is being rolled forward\r
4447         * Play moves forward\r
4448         */\r
4449        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4450                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4451        /* Mouse Wheel is being rolled backward\r
4452         * Play moves backward\r
4453         */\r
4454        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4455                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4456     }\r
4457     break;\r
4458 \r
4459   case WM_MBUTTONUP:\r
4460   case WM_RBUTTONUP:\r
4461     ReleaseCapture();\r
4462     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4463     break;\r
4464  \r
4465   case WM_MBUTTONDOWN:\r
4466   case WM_RBUTTONDOWN:\r
4467     ErrorPopDown();\r
4468     ReleaseCapture();\r
4469     fromX = fromY = -1;\r
4470     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4471     dragInfo.start.x = dragInfo.start.y = -1;\r
4472     dragInfo.from = dragInfo.start;\r
4473     dragInfo.lastpos = dragInfo.pos;\r
4474     if (appData.highlightDragging) {\r
4475       ClearHighlights();\r
4476     }\r
4477     if(y == -2) {\r
4478       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4479       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4480           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4481       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4482           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4483       }\r
4484       break;\r
4485     }\r
4486     DrawPosition(TRUE, NULL);\r
4487 \r
4488     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4489     switch (menuNr) {\r
4490     case 0:\r
4491       if (message == WM_MBUTTONDOWN) {\r
4492         buttonCount = 3;  /* even if system didn't think so */\r
4493         if (wParam & MK_SHIFT) \r
4494           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4495         else\r
4496           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4497       } else { /* message == WM_RBUTTONDOWN */\r
4498         /* Just have one menu, on the right button.  Windows users don't\r
4499            think to try the middle one, and sometimes other software steals\r
4500            it, or it doesn't really exist. */\r
4501         if(gameInfo.variant != VariantShogi)\r
4502             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4503         else\r
4504             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4505       }\r
4506       break;\r
4507     case 2:\r
4508       SetCapture(hwndMain);\r
4509       break;\r
4510     case 1:\r
4511       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4512       SetupDropMenu(hmenu);\r
4513       MenuPopup(hwnd, pt, hmenu, -1);\r
4514     default:\r
4515       break;\r
4516     }\r
4517     break;\r
4518   }\r
4519 \r
4520   recursive--;\r
4521 }\r
4522 \r
4523 /* Preprocess messages for buttons in main window */\r
4524 LRESULT CALLBACK\r
4525 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4526 {\r
4527   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4528   int i, dir;\r
4529 \r
4530   for (i=0; i<N_BUTTONS; i++) {\r
4531     if (buttonDesc[i].id == id) break;\r
4532   }\r
4533   if (i == N_BUTTONS) return 0;\r
4534   switch (message) {\r
4535   case WM_KEYDOWN:\r
4536     switch (wParam) {\r
4537     case VK_LEFT:\r
4538     case VK_RIGHT:\r
4539       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4540       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4541       return TRUE;\r
4542     }\r
4543     break;\r
4544   case WM_CHAR:\r
4545     switch (wParam) {\r
4546     case '\r':\r
4547       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4548       return TRUE;\r
4549     default:\r
4550       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4551         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4552         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4553         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4554         SetFocus(h);\r
4555         SendMessage(h, WM_CHAR, wParam, lParam);\r
4556         return TRUE;\r
4557       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4558         TypeInEvent((char)wParam);\r
4559       }\r
4560       break;\r
4561     }\r
4562     break;\r
4563   }\r
4564   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4565 }\r
4566 \r
4567 static int promoStyle;\r
4568 \r
4569 /* Process messages for Promotion dialog box */\r
4570 LRESULT CALLBACK\r
4571 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4572 {\r
4573 \r
4574   char promoChar;\r
4575 \r
4576   switch (message) {\r
4577 \r
4578   case WM_INITDIALOG: /* message: initialize dialog box */\r
4579     /* Center the dialog over the application window */\r
4580     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4581     Translate(hDlg, DLG_PromotionKing);\r
4582     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4583       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4584        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4585        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4586                SW_SHOW : SW_HIDE);\r
4587     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4588     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4589        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4590          PieceToChar(WhiteAngel) != '~') ||\r
4591         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4592          PieceToChar(BlackAngel) != '~')   ) ?\r
4593                SW_SHOW : SW_HIDE);\r
4594     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4595        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4596          PieceToChar(WhiteMarshall) != '~') ||\r
4597         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4598          PieceToChar(BlackMarshall) != '~')   ) ?\r
4599                SW_SHOW : SW_HIDE);\r
4600     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4601     ShowWindow(GetDlgItem(hDlg, PB_Rook),   !promoStyle ? SW_SHOW : SW_HIDE);\r
4602     ShowWindow(GetDlgItem(hDlg, PB_Bishop), !promoStyle ? SW_SHOW : SW_HIDE);\r
4603     if(promoStyle) {\r
4604         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4605         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4606         SetWindowText(hDlg, "Promote?");\r
4607     }\r
4608     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4609        gameInfo.variant == VariantSuper ?\r
4610                SW_SHOW : SW_HIDE);\r
4611     return TRUE;\r
4612 \r
4613   case WM_COMMAND: /* message: received a command */\r
4614     switch (LOWORD(wParam)) {\r
4615     case IDCANCEL:\r
4616       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4617       ClearHighlights();\r
4618       DrawPosition(FALSE, NULL);\r
4619       return TRUE;\r
4620     case PB_King:\r
4621       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4622       break;\r
4623     case PB_Queen:\r
4624       promoChar = promoStyle ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4625       break;\r
4626     case PB_Rook:\r
4627       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4628       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4629       break;\r
4630     case PB_Bishop:\r
4631       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4632       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4633       break;\r
4634     case PB_Chancellor:\r
4635       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4636       break;\r
4637     case PB_Archbishop:\r
4638       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4639       break;\r
4640     case PB_Knight:\r
4641       promoChar = gameInfo.variant == VariantShogi ? '=' : promoStyle ? NULLCHAR : \r
4642                   ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight));\r
4643       break;\r
4644     default:\r
4645       return FALSE;\r
4646     }\r
4647     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4648     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4649     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4650     fromX = fromY = -1;\r
4651     if (!appData.highlightLastMove) {\r
4652       ClearHighlights();\r
4653       DrawPosition(FALSE, NULL);\r
4654     }\r
4655     return TRUE;\r
4656   }\r
4657   return FALSE;\r
4658 }\r
4659 \r
4660 /* Pop up promotion dialog */\r
4661 VOID\r
4662 PromotionPopup(HWND hwnd)\r
4663 {\r
4664   FARPROC lpProc;\r
4665 \r
4666   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4667   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4668     hwnd, (DLGPROC)lpProc);\r
4669   FreeProcInstance(lpProc);\r
4670 }\r
4671 \r
4672 void\r
4673 PromotionPopUp(char choice)\r
4674 {\r
4675   promoStyle = (choice == '+' || IS_SHOGI(gameInfo.variant));\r
4676   DrawPosition(TRUE, NULL);\r
4677   PromotionPopup(hwndMain);\r
4678 }\r
4679 \r
4680 VOID\r
4681 LoadGameDialog(HWND hwnd, char* title)\r
4682 {\r
4683   UINT number = 0;\r
4684   FILE *f;\r
4685   char fileTitle[MSG_SIZ];\r
4686   f = OpenFileDialog(hwnd, "rb", "",\r
4687                      appData.oldSaveStyle ? "gam" : "pgn",\r
4688                      GAME_FILT,\r
4689                      title, &number, fileTitle, NULL);\r
4690   if (f != NULL) {\r
4691     cmailMsgLoaded = FALSE;\r
4692     if (number == 0) {\r
4693       int error = GameListBuild(f);\r
4694       if (error) {\r
4695         DisplayError(_("Cannot build game list"), error);\r
4696       } else if (!ListEmpty(&gameList) &&\r
4697                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4698         GameListPopUp(f, fileTitle);\r
4699         return;\r
4700       }\r
4701       GameListDestroy();\r
4702       number = 1;\r
4703     }\r
4704     LoadGame(f, number, fileTitle, FALSE);\r
4705   }\r
4706 }\r
4707 \r
4708 int get_term_width()\r
4709 {\r
4710     HDC hdc;\r
4711     TEXTMETRIC tm;\r
4712     RECT rc;\r
4713     HFONT hfont, hold_font;\r
4714     LOGFONT lf;\r
4715     HWND hText;\r
4716 \r
4717     if (hwndConsole)\r
4718         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4719     else\r
4720         return 79;\r
4721 \r
4722     // get the text metrics\r
4723     hdc = GetDC(hText);\r
4724     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4725     if (consoleCF.dwEffects & CFE_BOLD)\r
4726         lf.lfWeight = FW_BOLD;\r
4727     if (consoleCF.dwEffects & CFE_ITALIC)\r
4728         lf.lfItalic = TRUE;\r
4729     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4730         lf.lfStrikeOut = TRUE;\r
4731     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4732         lf.lfUnderline = TRUE;\r
4733     hfont = CreateFontIndirect(&lf);\r
4734     hold_font = SelectObject(hdc, hfont);\r
4735     GetTextMetrics(hdc, &tm);\r
4736     SelectObject(hdc, hold_font);\r
4737     DeleteObject(hfont);\r
4738     ReleaseDC(hText, hdc);\r
4739 \r
4740     // get the rectangle\r
4741     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4742 \r
4743     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4744 }\r
4745 \r
4746 void UpdateICSWidth(HWND hText)\r
4747 {\r
4748     LONG old_width, new_width;\r
4749 \r
4750     new_width = get_term_width(hText, FALSE);\r
4751     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4752     if (new_width != old_width)\r
4753     {\r
4754         ics_update_width(new_width);\r
4755         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4756     }\r
4757 }\r
4758 \r
4759 VOID\r
4760 ChangedConsoleFont()\r
4761 {\r
4762   CHARFORMAT cfmt;\r
4763   CHARRANGE tmpsel, sel;\r
4764   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4765   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4766   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4767   PARAFORMAT paraf;\r
4768 \r
4769   cfmt.cbSize = sizeof(CHARFORMAT);\r
4770   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4771     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4772                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4773   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4774    * size.  This was undocumented in the version of MSVC++ that I had\r
4775    * when I wrote the code, but is apparently documented now.\r
4776    */\r
4777   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4778   cfmt.bCharSet = f->lf.lfCharSet;\r
4779   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4780   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4781   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4782   /* Why are the following seemingly needed too? */\r
4783   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4784   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4785   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4786   tmpsel.cpMin = 0;\r
4787   tmpsel.cpMax = -1; /*999999?*/\r
4788   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4789   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4790   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4791    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4792    */\r
4793   paraf.cbSize = sizeof(paraf);\r
4794   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4795   paraf.dxStartIndent = 0;\r
4796   paraf.dxOffset = WRAP_INDENT;\r
4797   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4798   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4799   UpdateICSWidth(hText);\r
4800 }\r
4801 \r
4802 /*---------------------------------------------------------------------------*\\r
4803  *\r
4804  * Window Proc for main window\r
4805  *\r
4806 \*---------------------------------------------------------------------------*/\r
4807 \r
4808 /* Process messages for main window, etc. */\r
4809 LRESULT CALLBACK\r
4810 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4811 {\r
4812   FARPROC lpProc;\r
4813   int wmId;\r
4814   char *defName;\r
4815   FILE *f;\r
4816   UINT number;\r
4817   char fileTitle[MSG_SIZ];\r
4818   static SnapData sd;\r
4819   static int peek=0;\r
4820 \r
4821   switch (message) {\r
4822 \r
4823   case WM_PAINT: /* message: repaint portion of window */\r
4824     PaintProc(hwnd);\r
4825     break;\r
4826 \r
4827   case WM_ERASEBKGND:\r
4828     if (IsIconic(hwnd)) {\r
4829       /* Cheat; change the message */\r
4830       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4831     } else {\r
4832       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4833     }\r
4834     break;\r
4835 \r
4836   case WM_LBUTTONDOWN:\r
4837   case WM_MBUTTONDOWN:\r
4838   case WM_RBUTTONDOWN:\r
4839   case WM_LBUTTONUP:\r
4840   case WM_MBUTTONUP:\r
4841   case WM_RBUTTONUP:\r
4842   case WM_MOUSEMOVE:\r
4843   case WM_MOUSEWHEEL:\r
4844     MouseEvent(hwnd, message, wParam, lParam);\r
4845     break;\r
4846 \r
4847   case WM_KEYUP:\r
4848     if((char)wParam == '\b') {\r
4849       ForwardEvent(); peek = 0;\r
4850     }\r
4851 \r
4852     JAWS_KBUP_NAVIGATION\r
4853 \r
4854     break;\r
4855 \r
4856   case WM_KEYDOWN:\r
4857     if((char)wParam == '\b') {\r
4858       if(!peek) BackwardEvent(), peek = 1;\r
4859     }\r
4860 \r
4861     JAWS_KBDOWN_NAVIGATION\r
4862 \r
4863     break;\r
4864 \r
4865   case WM_CHAR:\r
4866     \r
4867     JAWS_ALT_INTERCEPT\r
4868 \r
4869     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4870         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4871         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4872         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4873         SetFocus(h);\r
4874         SendMessage(h, message, wParam, lParam);\r
4875     } else if(lParam != KF_REPEAT) {\r
4876         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4877                 TypeInEvent((char)wParam);\r
4878         } else if((char)wParam == 003) CopyGameToClipboard();\r
4879          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4880     }\r
4881 \r
4882     break;\r
4883 \r
4884   case WM_PALETTECHANGED:\r
4885     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4886       int nnew;\r
4887       HDC hdc = GetDC(hwndMain);\r
4888       SelectPalette(hdc, hPal, TRUE);\r
4889       nnew = RealizePalette(hdc);\r
4890       if (nnew > 0) {\r
4891         paletteChanged = TRUE;\r
4892 \r
4893         InvalidateRect(hwnd, &boardRect, FALSE);\r
4894       }\r
4895       ReleaseDC(hwnd, hdc);\r
4896     }\r
4897     break;\r
4898 \r
4899   case WM_QUERYNEWPALETTE:\r
4900     if (!appData.monoMode /*&& paletteChanged*/) {\r
4901       int nnew;\r
4902       HDC hdc = GetDC(hwndMain);\r
4903       paletteChanged = FALSE;\r
4904       SelectPalette(hdc, hPal, FALSE);\r
4905       nnew = RealizePalette(hdc);\r
4906       if (nnew > 0) {\r
4907         InvalidateRect(hwnd, &boardRect, FALSE);\r
4908       }\r
4909       ReleaseDC(hwnd, hdc);\r
4910       return TRUE;\r
4911     }\r
4912     return FALSE;\r
4913 \r
4914   case WM_COMMAND: /* message: command from application menu */\r
4915     wmId    = LOWORD(wParam);\r
4916 \r
4917     switch (wmId) {\r
4918     case IDM_NewGame:\r
4919       ResetGameEvent();\r
4920       SAY("new game enter a move to play against the computer with white");\r
4921       break;\r
4922 \r
4923     case IDM_NewGameFRC:\r
4924       if( NewGameFRC() == 0 ) {\r
4925         ResetGameEvent();\r
4926       }\r
4927       break;\r
4928 \r
4929     case IDM_NewVariant:\r
4930       NewVariantPopup(hwnd);\r
4931       break;\r
4932 \r
4933     case IDM_LoadGame:\r
4934       LoadGameDialog(hwnd, _("Load Game from File"));\r
4935       break;\r
4936 \r
4937     case IDM_LoadNextGame:\r
4938       ReloadGame(1);\r
4939       break;\r
4940 \r
4941     case IDM_LoadPrevGame:\r
4942       ReloadGame(-1);\r
4943       break;\r
4944 \r
4945     case IDM_ReloadGame:\r
4946       ReloadGame(0);\r
4947       break;\r
4948 \r
4949     case IDM_LoadPosition:\r
4950       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4951         Reset(FALSE, TRUE);\r
4952       }\r
4953       number = 1;\r
4954       f = OpenFileDialog(hwnd, "rb", "",\r
4955                          appData.oldSaveStyle ? "pos" : "fen",\r
4956                          POSITION_FILT,\r
4957                          _("Load Position from File"), &number, fileTitle, NULL);\r
4958       if (f != NULL) {\r
4959         LoadPosition(f, number, fileTitle);\r
4960       }\r
4961       break;\r
4962 \r
4963     case IDM_LoadNextPosition:\r
4964       ReloadPosition(1);\r
4965       break;\r
4966 \r
4967     case IDM_LoadPrevPosition:\r
4968       ReloadPosition(-1);\r
4969       break;\r
4970 \r
4971     case IDM_ReloadPosition:\r
4972       ReloadPosition(0);\r
4973       break;\r
4974 \r
4975     case IDM_SaveGame:\r
4976       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4977       f = OpenFileDialog(hwnd, "a", defName,\r
4978                          appData.oldSaveStyle ? "gam" : "pgn",\r
4979                          GAME_FILT,\r
4980                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4981       if (f != NULL) {\r
4982         SaveGame(f, 0, "");\r
4983       }\r
4984       break;\r
4985 \r
4986     case IDM_SavePosition:\r
4987       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4988       f = OpenFileDialog(hwnd, "a", defName,\r
4989                          appData.oldSaveStyle ? "pos" : "fen",\r
4990                          POSITION_FILT,\r
4991                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4992       if (f != NULL) {\r
4993         SavePosition(f, 0, "");\r
4994       }\r
4995       break;\r
4996 \r
4997     case IDM_SaveDiagram:\r
4998       defName = "diagram";\r
4999       f = OpenFileDialog(hwnd, "wb", defName,\r
5000                          "bmp",\r
5001                          DIAGRAM_FILT,\r
5002                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
5003       if (f != NULL) {\r
5004         SaveDiagram(f);\r
5005       }\r
5006       break;\r
5007 \r
5008     case IDM_SaveSelected:\r
5009       f = OpenFileDialog(hwnd, "a", "",\r
5010                          "pgn",\r
5011                          GAME_FILT,\r
5012                          _("Save Game to File"), NULL, fileTitle, NULL);\r
5013       if (f != NULL) {\r
5014         SaveSelected(f, 0, "");\r
5015       }\r
5016       break;\r
5017 \r
5018     case IDM_CreateBook:\r
5019       CreateBookEvent();\r
5020       break;\r
5021 \r
5022     case IDM_CopyGame:\r
5023       CopyGameToClipboard();\r
5024       break;\r
5025 \r
5026     case IDM_PasteGame:\r
5027       PasteGameFromClipboard();\r
5028       break;\r
5029 \r
5030     case IDM_CopyGameListToClipboard:\r
5031       CopyGameListToClipboard();\r
5032       break;\r
5033 \r
5034     /* [AS] Autodetect FEN or PGN data */\r
5035     case IDM_PasteAny:\r
5036       PasteGameOrFENFromClipboard();\r
5037       break;\r
5038 \r
5039     /* [AS] Move history */\r
5040     case IDM_ShowMoveHistory:\r
5041         if( MoveHistoryIsUp() ) {\r
5042             MoveHistoryPopDown();\r
5043         }\r
5044         else {\r
5045             MoveHistoryPopUp();\r
5046         }\r
5047         break;\r
5048 \r
5049     /* [AS] Eval graph */\r
5050     case IDM_ShowEvalGraph:\r
5051         if( EvalGraphIsUp() ) {\r
5052             EvalGraphPopDown();\r
5053         }\r
5054         else {\r
5055             EvalGraphPopUp();\r
5056             SetFocus(hwndMain);\r
5057         }\r
5058         break;\r
5059 \r
5060     /* [AS] Engine output */\r
5061     case IDM_ShowEngineOutput:\r
5062         if( EngineOutputIsUp() ) {\r
5063             EngineOutputPopDown();\r
5064         }\r
5065         else {\r
5066             EngineOutputPopUp();\r
5067         }\r
5068         break;\r
5069 \r
5070     /* [AS] User adjudication */\r
5071     case IDM_UserAdjudication_White:\r
5072         UserAdjudicationEvent( +1 );\r
5073         break;\r
5074 \r
5075     case IDM_UserAdjudication_Black:\r
5076         UserAdjudicationEvent( -1 );\r
5077         break;\r
5078 \r
5079     case IDM_UserAdjudication_Draw:\r
5080         UserAdjudicationEvent( 0 );\r
5081         break;\r
5082 \r
5083     /* [AS] Game list options dialog */\r
5084     case IDM_GameListOptions:\r
5085       GameListOptions();\r
5086       break;\r
5087 \r
5088     case IDM_NewChat:\r
5089       ChatPopUp(NULL);\r
5090       break;\r
5091 \r
5092     case IDM_CopyPosition:\r
5093       CopyFENToClipboard();\r
5094       break;\r
5095 \r
5096     case IDM_PastePosition:\r
5097       PasteFENFromClipboard();\r
5098       break;\r
5099 \r
5100     case IDM_MailMove:\r
5101       MailMoveEvent();\r
5102       break;\r
5103 \r
5104     case IDM_ReloadCMailMsg:\r
5105       Reset(TRUE, TRUE);\r
5106       ReloadCmailMsgEvent(FALSE);\r
5107       break;\r
5108 \r
5109     case IDM_Minimize:\r
5110       ShowWindow(hwnd, SW_MINIMIZE);\r
5111       break;\r
5112 \r
5113     case IDM_Exit:\r
5114       ExitEvent(0);\r
5115       break;\r
5116 \r
5117     case IDM_MachineWhite:\r
5118       MachineWhiteEvent();\r
5119       /*\r
5120        * refresh the tags dialog only if it's visible\r
5121        */\r
5122       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5123           char *tags;\r
5124           tags = PGNTags(&gameInfo);\r
5125           TagsPopUp(tags, CmailMsg());\r
5126           free(tags);\r
5127       }\r
5128       SAY("computer starts playing white");\r
5129       break;\r
5130 \r
5131     case IDM_MachineBlack:\r
5132       MachineBlackEvent();\r
5133       /*\r
5134        * refresh the tags dialog only if it's visible\r
5135        */\r
5136       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5137           char *tags;\r
5138           tags = PGNTags(&gameInfo);\r
5139           TagsPopUp(tags, CmailMsg());\r
5140           free(tags);\r
5141       }\r
5142       SAY("computer starts playing black");\r
5143       break;\r
5144 \r
5145     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
5146       if(matchMode) EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_GRAYED);\r
5147       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
5148       break;\r
5149 \r
5150     case IDM_TwoMachines:\r
5151       TwoMachinesEvent();\r
5152       /*\r
5153 \r
5154        * refresh the tags dialog only if it's visible\r
5155        */\r
5156       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5157           char *tags;\r
5158           tags = PGNTags(&gameInfo);\r
5159           TagsPopUp(tags, CmailMsg());\r
5160           free(tags);\r
5161       }\r
5162       SAY("computer starts playing both sides");\r
5163       break;\r
5164 \r
5165     case IDM_AnalysisMode:\r
5166       if(AnalyzeModeEvent()) {\r
5167         SAY("analyzing current position");\r
5168       }\r
5169       break;\r
5170 \r
5171     case IDM_AnalyzeFile:\r
5172       AnalyzeFileEvent();\r
5173       break;\r
5174 \r
5175     case IDM_IcsClient:\r
5176       IcsClientEvent();\r
5177       break;\r
5178 \r
5179     case IDM_EditGame:\r
5180     case IDM_EditGame2:\r
5181       EditGameEvent();\r
5182       SAY("edit game");\r
5183       break;\r
5184 \r
5185     case IDM_EditPosition:\r
5186     case IDM_EditPosition2:\r
5187       EditPositionEvent();\r
5188       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
5189       break;\r
5190 \r
5191     case IDM_Training:\r
5192       TrainingEvent();\r
5193       break;\r
5194 \r
5195     case IDM_ShowGameList:\r
5196       ShowGameListProc();\r
5197       break;\r
5198 \r
5199     case IDM_EditProgs1:\r
5200       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
5201       break;\r
5202 \r
5203     case IDM_LoadProg1:\r
5204      LoadEnginePopUp(hwndMain, 0);\r
5205       break;\r
5206 \r
5207     case IDM_LoadProg2:\r
5208      LoadEnginePopUp(hwndMain, 1);\r
5209       break;\r
5210 \r
5211     case IDM_EditServers:\r
5212       EditTagsPopUp(icsNames, &icsNames);\r
5213       break;\r
5214 \r
5215     case IDM_EditTags:\r
5216     case IDM_Tags:\r
5217       EditTagsProc();\r
5218       break;\r
5219 \r
5220     case IDM_EditBook:\r
5221       EditBookEvent();\r
5222       break;\r
5223 \r
5224     case IDM_EditComment:\r
5225     case IDM_Comment:\r
5226       if (commentUp && editComment) {\r
5227         CommentPopDown();\r
5228       } else {\r
5229         EditCommentEvent();\r
5230       }\r
5231       break;\r
5232 \r
5233     case IDM_Pause:\r
5234       PauseEvent();\r
5235       break;\r
5236 \r
5237     case IDM_Accept:\r
5238       AcceptEvent();\r
5239       break;\r
5240 \r
5241     case IDM_Decline:\r
5242       DeclineEvent();\r
5243       break;\r
5244 \r
5245     case IDM_Rematch:\r
5246 \r
5247       RematchEvent();\r
5248       break;\r
5249 \r
5250     case IDM_CallFlag:\r
5251       CallFlagEvent();\r
5252       break;\r
5253 \r
5254     case IDM_Draw:\r
5255       DrawEvent();\r
5256       break;\r
5257 \r
5258     case IDM_Adjourn:\r
5259       AdjournEvent();\r
5260       break;\r
5261 \r
5262     case IDM_Abort:\r
5263       AbortEvent();\r
5264       break;\r
5265 \r
5266     case IDM_Resign:\r
5267       ResignEvent();\r
5268       break;\r
5269 \r
5270     case IDM_StopObserving:\r
5271       StopObservingEvent();\r
5272       break;\r
5273 \r
5274     case IDM_StopExamining:\r
5275       StopExaminingEvent();\r
5276       break;\r
5277 \r
5278     case IDM_Upload:\r
5279       UploadGameEvent();\r
5280       break;\r
5281 \r
5282     case IDM_TypeInMove:\r
5283       TypeInEvent('\000');\r
5284       break;\r
5285 \r
5286     case IDM_TypeInName:\r
5287       PopUpNameDialog('\000');\r
5288       break;\r
5289 \r
5290     case IDM_Backward:\r
5291       BackwardEvent();\r
5292       SetFocus(hwndMain);\r
5293       break;\r
5294 \r
5295     JAWS_MENU_ITEMS\r
5296 \r
5297     case IDM_Forward:\r
5298       ForwardEvent();\r
5299       SetFocus(hwndMain);\r
5300       break;\r
5301 \r
5302     case IDM_ToStart:\r
5303       ToStartEvent();\r
5304       SetFocus(hwndMain);\r
5305       break;\r
5306 \r
5307     case IDM_ToEnd:\r
5308       ToEndEvent();\r
5309       SetFocus(hwndMain);\r
5310       break;\r
5311 \r
5312     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5313     case OPT_GameListPrev:\r
5314       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5315       break;\r
5316 \r
5317     case IDM_Revert:\r
5318       RevertEvent(FALSE);\r
5319       break;\r
5320 \r
5321     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5322       RevertEvent(TRUE);\r
5323       break;\r
5324 \r
5325     case IDM_TruncateGame:\r
5326       TruncateGameEvent();\r
5327       break;\r
5328 \r
5329     case IDM_MoveNow:\r
5330       MoveNowEvent();\r
5331       break;\r
5332 \r
5333     case IDM_RetractMove:\r
5334       RetractMoveEvent();\r
5335       break;\r
5336 \r
5337     case IDM_FlipView:\r
5338       flipView = !flipView;\r
5339       DrawPosition(FALSE, NULL);\r
5340       break;\r
5341 \r
5342     case IDM_FlipClock:\r
5343       flipClock = !flipClock;\r
5344       DisplayBothClocks();\r
5345       DisplayLogos();\r
5346       break;\r
5347 \r
5348     case IDM_MuteSounds:\r
5349       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5350       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5351                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5352       break;\r
5353 \r
5354     case IDM_GeneralOptions:\r
5355       GeneralOptionsPopup(hwnd);\r
5356       DrawPosition(TRUE, NULL);\r
5357       break;\r
5358 \r
5359     case IDM_BoardOptions:\r
5360       BoardOptionsPopup(hwnd);\r
5361       break;\r
5362 \r
5363     case IDM_ThemeOptions:\r
5364       ThemeOptionsPopup(hwnd);\r
5365       break;\r
5366 \r
5367     case IDM_EnginePlayOptions:\r
5368       EnginePlayOptionsPopup(hwnd);\r
5369       break;\r
5370 \r
5371     case IDM_Engine1Options:\r
5372       EngineOptionsPopup(hwnd, &first);\r
5373       break;\r
5374 \r
5375     case IDM_Engine2Options:\r
5376       savedHwnd = hwnd;\r
5377       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5378       EngineOptionsPopup(hwnd, &second);\r
5379       break;\r
5380 \r
5381     case IDM_OptionsUCI:\r
5382       UciOptionsPopup(hwnd);\r
5383       break;\r
5384 \r
5385     case IDM_Tourney:\r
5386       TourneyPopup(hwnd);\r
5387       break;\r
5388 \r
5389     case IDM_IcsOptions:\r
5390       IcsOptionsPopup(hwnd);\r
5391       break;\r
5392 \r
5393     case IDM_Fonts:\r
5394       FontsOptionsPopup(hwnd);\r
5395       break;\r
5396 \r
5397     case IDM_Sounds:\r
5398       SoundOptionsPopup(hwnd);\r
5399       break;\r
5400 \r
5401     case IDM_CommPort:\r
5402       CommPortOptionsPopup(hwnd);\r
5403       break;\r
5404 \r
5405     case IDM_LoadOptions:\r
5406       LoadOptionsPopup(hwnd);\r
5407       break;\r
5408 \r
5409     case IDM_SaveOptions:\r
5410       SaveOptionsPopup(hwnd);\r
5411       break;\r
5412 \r
5413     case IDM_TimeControl:\r
5414       TimeControlOptionsPopup(hwnd);\r
5415       break;\r
5416 \r
5417     case IDM_SaveSettings:\r
5418       SaveSettings(settingsFileName);\r
5419       break;\r
5420 \r
5421     case IDM_SaveSettingsOnExit:\r
5422       saveSettingsOnExit = !saveSettingsOnExit;\r
5423       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5424                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5425                                          MF_CHECKED : MF_UNCHECKED));\r
5426       break;\r
5427 \r
5428     case IDM_Hint:\r
5429       HintEvent();\r
5430       break;\r
5431 \r
5432     case IDM_Book:\r
5433       BookEvent();\r
5434       break;\r
5435 \r
5436     case IDM_AboutGame:\r
5437       AboutGameEvent();\r
5438       break;\r
5439 \r
5440     case IDM_Debug:\r
5441       appData.debugMode = !appData.debugMode;\r
5442       if (appData.debugMode) {\r
5443         char dir[MSG_SIZ];\r
5444         GetCurrentDirectory(MSG_SIZ, dir);\r
5445         SetCurrentDirectory(installDir);\r
5446         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5447         SetCurrentDirectory(dir);\r
5448         setbuf(debugFP, NULL);\r
5449       } else {\r
5450         fclose(debugFP);\r
5451         debugFP = NULL;\r
5452       }\r
5453       break;\r
5454 \r
5455     case IDM_HELPCONTENTS:\r
5456       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5457           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5458           MessageBox (GetFocus(),\r
5459                     _("Unable to activate help"),\r
5460                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5461       }\r
5462       break;\r
5463 \r
5464     case IDM_HELPSEARCH:\r
5465         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5466             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5467         MessageBox (GetFocus(),\r
5468                     _("Unable to activate help"),\r
5469                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5470       }\r
5471       break;\r
5472 \r
5473     case IDM_HELPHELP:\r
5474       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5475         MessageBox (GetFocus(),\r
5476                     _("Unable to activate help"),\r
5477                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5478       }\r
5479       break;\r
5480 \r
5481     case IDM_ABOUT:\r
5482       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5483       DialogBox(hInst, \r
5484         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5485         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5486       FreeProcInstance(lpProc);\r
5487       break;\r
5488 \r
5489     case IDM_DirectCommand1:\r
5490       AskQuestionEvent(_("Direct Command"),\r
5491                        _("Send to chess program:"), "", "1");\r
5492       break;\r
5493     case IDM_DirectCommand2:\r
5494       AskQuestionEvent(_("Direct Command"),\r
5495                        _("Send to second chess program:"), "", "2");\r
5496       break;\r
5497 \r
5498     case EP_WhitePawn:\r
5499       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5500       fromX = fromY = -1;\r
5501       break;\r
5502 \r
5503     case EP_WhiteKnight:\r
5504       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5505       fromX = fromY = -1;\r
5506       break;\r
5507 \r
5508     case EP_WhiteBishop:\r
5509       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5510       fromX = fromY = -1;\r
5511       break;\r
5512 \r
5513     case EP_WhiteRook:\r
5514       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5515       fromX = fromY = -1;\r
5516       break;\r
5517 \r
5518     case EP_WhiteQueen:\r
5519       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5520       fromX = fromY = -1;\r
5521       break;\r
5522 \r
5523     case EP_WhiteFerz:\r
5524       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5525       fromX = fromY = -1;\r
5526       break;\r
5527 \r
5528     case EP_WhiteWazir:\r
5529       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5530       fromX = fromY = -1;\r
5531       break;\r
5532 \r
5533     case EP_WhiteAlfil:\r
5534       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5535       fromX = fromY = -1;\r
5536       break;\r
5537 \r
5538     case EP_WhiteCannon:\r
5539       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5540       fromX = fromY = -1;\r
5541       break;\r
5542 \r
5543     case EP_WhiteCardinal:\r
5544       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5545       fromX = fromY = -1;\r
5546       break;\r
5547 \r
5548     case EP_WhiteMarshall:\r
5549       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5550       fromX = fromY = -1;\r
5551       break;\r
5552 \r
5553     case EP_WhiteKing:\r
5554       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5555       fromX = fromY = -1;\r
5556       break;\r
5557 \r
5558     case EP_BlackPawn:\r
5559       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5560       fromX = fromY = -1;\r
5561       break;\r
5562 \r
5563     case EP_BlackKnight:\r
5564       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5565       fromX = fromY = -1;\r
5566       break;\r
5567 \r
5568     case EP_BlackBishop:\r
5569       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5570       fromX = fromY = -1;\r
5571       break;\r
5572 \r
5573     case EP_BlackRook:\r
5574       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5575       fromX = fromY = -1;\r
5576       break;\r
5577 \r
5578     case EP_BlackQueen:\r
5579       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5580       fromX = fromY = -1;\r
5581       break;\r
5582 \r
5583     case EP_BlackFerz:\r
5584       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5585       fromX = fromY = -1;\r
5586       break;\r
5587 \r
5588     case EP_BlackWazir:\r
5589       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5590       fromX = fromY = -1;\r
5591       break;\r
5592 \r
5593     case EP_BlackAlfil:\r
5594       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5595       fromX = fromY = -1;\r
5596       break;\r
5597 \r
5598     case EP_BlackCannon:\r
5599       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5600       fromX = fromY = -1;\r
5601       break;\r
5602 \r
5603     case EP_BlackCardinal:\r
5604       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5605       fromX = fromY = -1;\r
5606       break;\r
5607 \r
5608     case EP_BlackMarshall:\r
5609       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5610       fromX = fromY = -1;\r
5611       break;\r
5612 \r
5613     case EP_BlackKing:\r
5614       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5615       fromX = fromY = -1;\r
5616       break;\r
5617 \r
5618     case EP_EmptySquare:\r
5619       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5620       fromX = fromY = -1;\r
5621       break;\r
5622 \r
5623     case EP_ClearBoard:\r
5624       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5625       fromX = fromY = -1;\r
5626       break;\r
5627 \r
5628     case EP_White:\r
5629       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5630       fromX = fromY = -1;\r
5631       break;\r
5632 \r
5633     case EP_Black:\r
5634       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5635       fromX = fromY = -1;\r
5636       break;\r
5637 \r
5638     case EP_Promote:\r
5639       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5640       fromX = fromY = -1;\r
5641       break;\r
5642 \r
5643     case EP_Demote:\r
5644       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5645       fromX = fromY = -1;\r
5646       break;\r
5647 \r
5648     case DP_Pawn:\r
5649       DropMenuEvent(WhitePawn, fromX, fromY);\r
5650       fromX = fromY = -1;\r
5651       break;\r
5652 \r
5653     case DP_Knight:\r
5654       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5655       fromX = fromY = -1;\r
5656       break;\r
5657 \r
5658     case DP_Bishop:\r
5659       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5660       fromX = fromY = -1;\r
5661       break;\r
5662 \r
5663     case DP_Rook:\r
5664       DropMenuEvent(WhiteRook, fromX, fromY);\r
5665       fromX = fromY = -1;\r
5666       break;\r
5667 \r
5668     case DP_Queen:\r
5669       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5670       fromX = fromY = -1;\r
5671       break;\r
5672 \r
5673     case IDM_English:\r
5674       barbaric = 0; appData.language = "";\r
5675       TranslateMenus(0);\r
5676       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5677       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5678       lastChecked = wmId;\r
5679       break;\r
5680 \r
5681     default:\r
5682       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5683           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5684       else\r
5685       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5686           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5687           TranslateMenus(0);\r
5688           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5689           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5690           lastChecked = wmId;\r
5691           break;\r
5692       }\r
5693       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5694     }\r
5695     break;\r
5696 \r
5697   case WM_TIMER:\r
5698     switch (wParam) {\r
5699     case CLOCK_TIMER_ID:\r
5700       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5701       clockTimerEvent = 0;\r
5702       DecrementClocks(); /* call into back end */\r
5703       break;\r
5704     case LOAD_GAME_TIMER_ID:\r
5705       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5706       loadGameTimerEvent = 0;\r
5707       AutoPlayGameLoop(); /* call into back end */\r
5708       break;\r
5709     case ANALYSIS_TIMER_ID:\r
5710       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5711                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5712         AnalysisPeriodicEvent(0);\r
5713       } else {\r
5714         KillTimer(hwnd, analysisTimerEvent);\r
5715         analysisTimerEvent = 0;\r
5716       }\r
5717       break;\r
5718     case DELAYED_TIMER_ID:\r
5719       KillTimer(hwnd, delayedTimerEvent);\r
5720       delayedTimerEvent = 0;\r
5721       delayedTimerCallback();\r
5722       break;\r
5723     }\r
5724     break;\r
5725 \r
5726   case WM_USER_Input:\r
5727     InputEvent(hwnd, message, wParam, lParam);\r
5728     break;\r
5729 \r
5730   /* [AS] Also move "attached" child windows */\r
5731   case WM_WINDOWPOSCHANGING:\r
5732 \r
5733     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5734         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5735 \r
5736         if( ((lpwp->flags & SWP_NOMOVE) == 0) /*&& ((lpwp->flags & SWP_NOSIZE) != 0)*/ ) { // [HGM] in Win8 size always accompanies move?\r
5737             /* Window is moving */\r
5738             RECT rcMain;\r
5739 \r
5740 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5741             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5742             rcMain.right  = wpMain.x + wpMain.width;\r
5743             rcMain.top    = wpMain.y;\r
5744             rcMain.bottom = wpMain.y + wpMain.height;\r
5745             \r
5746             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5747             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5748             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5749             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5750             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5751             wpMain.x = lpwp->x;\r
5752             wpMain.y = lpwp->y;\r
5753 \r
5754         }\r
5755     }\r
5756     break;\r
5757 \r
5758   /* [AS] Snapping */\r
5759   case WM_ENTERSIZEMOVE:\r
5760     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5761     if (hwnd == hwndMain) {\r
5762       doingSizing = TRUE;\r
5763       lastSizing = 0;\r
5764     }\r
5765     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5766     break;\r
5767 \r
5768   case WM_SIZING:\r
5769     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5770     if (hwnd == hwndMain) {\r
5771       lastSizing = wParam;\r
5772     }\r
5773     break;\r
5774 \r
5775   case WM_MOVING:\r
5776     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5777       return OnMoving( &sd, hwnd, wParam, lParam );\r
5778 \r
5779   case WM_EXITSIZEMOVE:\r
5780     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5781     if (hwnd == hwndMain) {\r
5782       RECT client;\r
5783       doingSizing = FALSE;\r
5784       InvalidateRect(hwnd, &boardRect, FALSE);\r
5785       GetClientRect(hwnd, &client);\r
5786       ResizeBoard(client.right, client.bottom, lastSizing);\r
5787       lastSizing = 0;\r
5788       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5789     }\r
5790     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5791     break;\r
5792 \r
5793   case WM_DESTROY: /* message: window being destroyed */\r
5794     PostQuitMessage(0);\r
5795     break;\r
5796 \r
5797   case WM_CLOSE:\r
5798     if (hwnd == hwndMain) {\r
5799       ExitEvent(0);\r
5800     }\r
5801     break;\r
5802 \r
5803   default:      /* Passes it on if unprocessed */\r
5804     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5805   }\r
5806 \r
5807 \r
5808   return 0;\r
5809 }\r
5810 \r
5811 /*---------------------------------------------------------------------------*\\r
5812  *\r
5813  * Misc utility routines\r
5814  *\r
5815 \*---------------------------------------------------------------------------*/\r
5816 \r
5817 /*\r
5818  * Decent random number generator, at least not as bad as Windows\r
5819  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5820  */\r
5821 unsigned int randstate;\r
5822 \r
5823 int\r
5824 myrandom(void)\r
5825 {\r
5826   randstate = randstate * 1664525 + 1013904223;\r
5827   return (int) randstate & 0x7fffffff;\r
5828 }\r
5829 \r
5830 void\r
5831 mysrandom(unsigned int seed)\r
5832 {\r
5833   randstate = seed;\r
5834 }\r
5835 \r
5836 \r
5837 /* \r
5838  * returns TRUE if user selects a different color, FALSE otherwise \r
5839  */\r
5840 \r
5841 BOOL\r
5842 ChangeColor(HWND hwnd, COLORREF *which)\r
5843 {\r
5844   static BOOL firstTime = TRUE;\r
5845   static DWORD customColors[16];\r
5846   CHOOSECOLOR cc;\r
5847   COLORREF newcolor;\r
5848   int i;\r
5849   ColorClass ccl;\r
5850 \r
5851   if (firstTime) {\r
5852     /* Make initial colors in use available as custom colors */\r
5853     /* Should we put the compiled-in defaults here instead? */\r
5854     i = 0;\r
5855     customColors[i++] = lightSquareColor & 0xffffff;\r
5856     customColors[i++] = darkSquareColor & 0xffffff;\r
5857     customColors[i++] = whitePieceColor & 0xffffff;\r
5858     customColors[i++] = blackPieceColor & 0xffffff;\r
5859     customColors[i++] = highlightSquareColor & 0xffffff;\r
5860     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5861 \r
5862     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5863       customColors[i++] = textAttribs[ccl].color;\r
5864     }\r
5865     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5866     firstTime = FALSE;\r
5867   }\r
5868 \r
5869   cc.lStructSize = sizeof(cc);\r
5870   cc.hwndOwner = hwnd;\r
5871   cc.hInstance = NULL;\r
5872   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5873   cc.lpCustColors = (LPDWORD) customColors;\r
5874   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5875 \r
5876   if (!ChooseColor(&cc)) return FALSE;\r
5877 \r
5878   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5879   if (newcolor == *which) return FALSE;\r
5880   *which = newcolor;\r
5881   return TRUE;\r
5882 \r
5883   /*\r
5884   InitDrawingColors();\r
5885   InvalidateRect(hwnd, &boardRect, FALSE);\r
5886   */\r
5887 }\r
5888 \r
5889 BOOLEAN\r
5890 MyLoadSound(MySound *ms)\r
5891 {\r
5892   BOOL ok = FALSE;\r
5893   struct stat st;\r
5894   FILE *f;\r
5895 \r
5896   if (ms->data && ms->flag) free(ms->data);\r
5897   ms->data = NULL;\r
5898 \r
5899   switch (ms->name[0]) {\r
5900   case NULLCHAR:\r
5901     /* Silence */\r
5902     ok = TRUE;\r
5903     break;\r
5904   case '$':\r
5905     /* System sound from Control Panel.  Don't preload here. */\r
5906     ok = TRUE;\r
5907     break;\r
5908   case '!':\r
5909     if (ms->name[1] == NULLCHAR) {\r
5910       /* "!" alone = silence */\r
5911       ok = TRUE;\r
5912     } else {\r
5913       /* Builtin wave resource.  Error if not found. */\r
5914       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5915       if (h == NULL) break;\r
5916       ms->data = (void *)LoadResource(hInst, h);\r
5917       ms->flag = 0; // not maloced, so cannot be freed!\r
5918       if (h == NULL) break;\r
5919       ok = TRUE;\r
5920     }\r
5921     break;\r
5922   default:\r
5923     /* .wav file.  Error if not found. */\r
5924     f = fopen(ms->name, "rb");\r
5925     if (f == NULL) break;\r
5926     if (fstat(fileno(f), &st) < 0) break;\r
5927     ms->data = malloc(st.st_size);\r
5928     ms->flag = 1;\r
5929     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5930     fclose(f);\r
5931     ok = TRUE;\r
5932     break;\r
5933   }\r
5934   if (!ok) {\r
5935     char buf[MSG_SIZ];\r
5936       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5937     DisplayError(buf, GetLastError());\r
5938   }\r
5939   return ok;\r
5940 }\r
5941 \r
5942 BOOLEAN\r
5943 MyPlaySound(MySound *ms)\r
5944 {\r
5945   BOOLEAN ok = FALSE;\r
5946 \r
5947   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5948   switch (ms->name[0]) {\r
5949   case NULLCHAR:\r
5950         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5951     /* Silence */\r
5952     ok = TRUE;\r
5953     break;\r
5954   case '$':\r
5955     /* System sound from Control Panel (deprecated feature).\r
5956        "$" alone or an unset sound name gets default beep (still in use). */\r
5957     if (ms->name[1]) {\r
5958       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5959     }\r
5960     if (!ok) ok = MessageBeep(MB_OK);\r
5961     break; \r
5962   case '!':\r
5963     /* Builtin wave resource, or "!" alone for silence */\r
5964     if (ms->name[1]) {\r
5965       if (ms->data == NULL) return FALSE;\r
5966       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5967     } else {\r
5968       ok = TRUE;\r
5969     }\r
5970     break;\r
5971   default:\r
5972     /* .wav file.  Error if not found. */\r
5973     if (ms->data == NULL) return FALSE;\r
5974     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5975     break;\r
5976   }\r
5977   /* Don't print an error: this can happen innocently if the sound driver\r
5978      is busy; for instance, if another instance of WinBoard is playing\r
5979      a sound at about the same time. */\r
5980   return ok;\r
5981 }\r
5982 \r
5983 \r
5984 LRESULT CALLBACK\r
5985 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5986 {\r
5987   BOOL ok;\r
5988   OPENFILENAME *ofn;\r
5989   static UINT *number; /* gross that this is static */\r
5990 \r
5991   switch (message) {\r
5992   case WM_INITDIALOG: /* message: initialize dialog box */\r
5993     /* Center the dialog over the application window */\r
5994     ofn = (OPENFILENAME *) lParam;\r
5995     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5996       number = (UINT *) ofn->lCustData;\r
5997       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5998     } else {\r
5999       number = NULL;\r
6000     }\r
6001     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6002     Translate(hDlg, 1536);\r
6003     return FALSE;  /* Allow for further processing */\r
6004 \r
6005   case WM_COMMAND:\r
6006     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6007       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6008     }\r
6009     return FALSE;  /* Allow for further processing */\r
6010   }\r
6011   return FALSE;\r
6012 }\r
6013 \r
6014 UINT APIENTRY\r
6015 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6016 {\r
6017   static UINT *number;\r
6018   OPENFILENAME *ofname;\r
6019   OFNOTIFY *ofnot;\r
6020   switch (uiMsg) {\r
6021   case WM_INITDIALOG:\r
6022     Translate(hdlg, DLG_IndexNumber);\r
6023     ofname = (OPENFILENAME *)lParam;\r
6024     number = (UINT *)(ofname->lCustData);\r
6025     break;\r
6026   case WM_NOTIFY:\r
6027     ofnot = (OFNOTIFY *)lParam;\r
6028     if (ofnot->hdr.code == CDN_FILEOK) {\r
6029       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6030     }\r
6031     break;\r
6032   }\r
6033   return 0;\r
6034 }\r
6035 \r
6036 \r
6037 FILE *\r
6038 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6039                char *nameFilt, char *dlgTitle, UINT *number,\r
6040                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6041 {\r
6042   OPENFILENAME openFileName;\r
6043   char buf1[MSG_SIZ];\r
6044   FILE *f;\r
6045 \r
6046   if (fileName == NULL) fileName = buf1;\r
6047   if (defName == NULL) {\r
6048     safeStrCpy(fileName, "*.", 3 );\r
6049     strcat(fileName, defExt);\r
6050   } else {\r
6051     safeStrCpy(fileName, defName, MSG_SIZ );\r
6052   }\r
6053     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
6054   if (number) *number = 0;\r
6055 \r
6056   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6057   openFileName.hwndOwner         = hwnd;\r
6058   openFileName.hInstance         = (HANDLE) hInst;\r
6059   openFileName.lpstrFilter       = nameFilt;\r
6060   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6061   openFileName.nMaxCustFilter    = 0L;\r
6062   openFileName.nFilterIndex      = 1L;\r
6063   openFileName.lpstrFile         = fileName;\r
6064   openFileName.nMaxFile          = MSG_SIZ;\r
6065   openFileName.lpstrFileTitle    = fileTitle;\r
6066   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6067   openFileName.lpstrInitialDir   = NULL;\r
6068   openFileName.lpstrTitle        = dlgTitle;\r
6069   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6070     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6071     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6072     | (oldDialog ? 0 : OFN_EXPLORER);\r
6073   openFileName.nFileOffset       = 0;\r
6074   openFileName.nFileExtension    = 0;\r
6075   openFileName.lpstrDefExt       = defExt;\r
6076   openFileName.lCustData         = (LONG) number;\r
6077   openFileName.lpfnHook          = oldDialog ?\r
6078     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6079   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6080 \r
6081   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6082                         GetOpenFileName(&openFileName)) {\r
6083     /* open the file */\r
6084     f = fopen(openFileName.lpstrFile, write);\r
6085     if (f == NULL) {\r
6086       MessageBox(hwnd, _("File open failed"), NULL,\r
6087                  MB_OK|MB_ICONEXCLAMATION);\r
6088       return NULL;\r
6089     }\r
6090   } else {\r
6091     int err = CommDlgExtendedError();\r
6092     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
6093     return FALSE;\r
6094   }\r
6095   return f;\r
6096 }\r
6097 \r
6098 \r
6099 \r
6100 VOID APIENTRY\r
6101 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6102 {\r
6103   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6104 \r
6105   /*\r
6106    * Get the first pop-up menu in the menu template. This is the\r
6107    * menu that TrackPopupMenu displays.\r
6108    */\r
6109   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6110   TranslateOneMenu(10, hmenuTrackPopup);\r
6111 \r
6112   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6113 \r
6114   /*\r
6115    * TrackPopup uses screen coordinates, so convert the\r
6116    * coordinates of the mouse click to screen coordinates.\r
6117    */\r
6118   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6119 \r
6120   /* Draw and track the floating pop-up menu. */\r
6121   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6122                  pt.x, pt.y, 0, hwnd, NULL);\r
6123 \r
6124   /* Destroy the menu.*/\r
6125   DestroyMenu(hmenu);\r
6126 }\r
6127    \r
6128 typedef struct {\r
6129   HWND hDlg, hText;\r
6130   int sizeX, sizeY, newSizeX, newSizeY;\r
6131   HDWP hdwp;\r
6132 } ResizeEditPlusButtonsClosure;\r
6133 \r
6134 BOOL CALLBACK\r
6135 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6136 {\r
6137   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6138   RECT rect;\r
6139   POINT pt;\r
6140 \r
6141   if (hChild == cl->hText) return TRUE;\r
6142   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6143   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6144   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6145   ScreenToClient(cl->hDlg, &pt);\r
6146   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6147     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6148   return TRUE;\r
6149 }\r
6150 \r
6151 /* Resize a dialog that has a (rich) edit field filling most of\r
6152    the top, with a row of buttons below */\r
6153 VOID\r
6154 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6155 {\r
6156   RECT rectText;\r
6157   int newTextHeight, newTextWidth;\r
6158   ResizeEditPlusButtonsClosure cl;\r
6159   \r
6160   /*if (IsIconic(hDlg)) return;*/\r
6161   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6162   \r
6163   cl.hdwp = BeginDeferWindowPos(8);\r
6164 \r
6165   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6166   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6167   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6168   if (newTextHeight < 0) {\r
6169     newSizeY += -newTextHeight;\r
6170     newTextHeight = 0;\r
6171   }\r
6172   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6173     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6174 \r
6175   cl.hDlg = hDlg;\r
6176   cl.hText = hText;\r
6177   cl.sizeX = sizeX;\r
6178   cl.sizeY = sizeY;\r
6179   cl.newSizeX = newSizeX;\r
6180   cl.newSizeY = newSizeY;\r
6181   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6182 \r
6183   EndDeferWindowPos(cl.hdwp);\r
6184 }\r
6185 \r
6186 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6187 {\r
6188     RECT    rChild, rParent;\r
6189     int     wChild, hChild, wParent, hParent;\r
6190     int     wScreen, hScreen, xNew, yNew;\r
6191     HDC     hdc;\r
6192 \r
6193     /* Get the Height and Width of the child window */\r
6194     GetWindowRect (hwndChild, &rChild);\r
6195     wChild = rChild.right - rChild.left;\r
6196     hChild = rChild.bottom - rChild.top;\r
6197 \r
6198     /* Get the Height and Width of the parent window */\r
6199     GetWindowRect (hwndParent, &rParent);\r
6200     wParent = rParent.right - rParent.left;\r
6201     hParent = rParent.bottom - rParent.top;\r
6202 \r
6203     /* Get the display limits */\r
6204     hdc = GetDC (hwndChild);\r
6205     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6206     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6207     ReleaseDC(hwndChild, hdc);\r
6208 \r
6209     /* Calculate new X position, then adjust for screen */\r
6210     xNew = rParent.left + ((wParent - wChild) /2);\r
6211     if (xNew < 0) {\r
6212         xNew = 0;\r
6213     } else if ((xNew+wChild) > wScreen) {\r
6214         xNew = wScreen - wChild;\r
6215     }\r
6216 \r
6217     /* Calculate new Y position, then adjust for screen */\r
6218     if( mode == 0 ) {\r
6219         yNew = rParent.top  + ((hParent - hChild) /2);\r
6220     }\r
6221     else {\r
6222         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6223     }\r
6224 \r
6225     if (yNew < 0) {\r
6226         yNew = 0;\r
6227     } else if ((yNew+hChild) > hScreen) {\r
6228         yNew = hScreen - hChild;\r
6229     }\r
6230 \r
6231     /* Set it, and return */\r
6232     return SetWindowPos (hwndChild, NULL,\r
6233                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6234 }\r
6235 \r
6236 /* Center one window over another */\r
6237 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6238 {\r
6239     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6240 }\r
6241 \r
6242 /*---------------------------------------------------------------------------*\\r
6243  *\r
6244  * Startup Dialog functions\r
6245  *\r
6246 \*---------------------------------------------------------------------------*/\r
6247 void\r
6248 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6249 {\r
6250   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6251 \r
6252   while (*cd != NULL) {\r
6253     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6254     cd++;\r
6255   }\r
6256 }\r
6257 \r
6258 void\r
6259 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6260 {\r
6261   char buf1[MAX_ARG_LEN];\r
6262   int len;\r
6263 \r
6264   if (str[0] == '@') {\r
6265     FILE* f = fopen(str + 1, "r");\r
6266     if (f == NULL) {\r
6267       DisplayFatalError(str + 1, errno, 2);\r
6268       return;\r
6269     }\r
6270     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6271     fclose(f);\r
6272     buf1[len] = NULLCHAR;\r
6273     str = buf1;\r
6274   }\r
6275 \r
6276 \r
6277   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6278 \r
6279   for (;;) {\r
6280     char buf[MSG_SIZ];\r
6281     char *end = strchr(str, '\n');\r
6282     if (end == NULL) return;\r
6283     memcpy(buf, str, end - str);\r
6284     buf[end - str] = NULLCHAR;\r
6285     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6286     str = end + 1;\r
6287   }\r
6288 }\r
6289 \r
6290 void\r
6291 SetStartupDialogEnables(HWND hDlg)\r
6292 {\r
6293   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6294     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6295     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6296   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6297     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6298   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6299     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6300   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6301     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6302   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6303     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6304     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6305     IsDlgButtonChecked(hDlg, OPT_View));\r
6306 }\r
6307 \r
6308 char *\r
6309 QuoteForFilename(char *filename)\r
6310 {\r
6311   int dquote, space;\r
6312   dquote = strchr(filename, '"') != NULL;\r
6313   space = strchr(filename, ' ') != NULL;\r
6314   if (dquote || space) {\r
6315     if (dquote) {\r
6316       return "'";\r
6317     } else {\r
6318       return "\"";\r
6319     }\r
6320   } else {\r
6321     return "";\r
6322   }\r
6323 }\r
6324 \r
6325 VOID\r
6326 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6327 {\r
6328   char buf[MSG_SIZ];\r
6329   char *q;\r
6330 \r
6331   InitComboStringsFromOption(hwndCombo, nthnames);\r
6332   q = QuoteForFilename(nthcp);\r
6333     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6334   if (*nthdir != NULLCHAR) {\r
6335     q = QuoteForFilename(nthdir);\r
6336       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6337   }\r
6338   if (*nthcp == NULLCHAR) {\r
6339     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6340   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6341     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6342     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6343   }\r
6344 }\r
6345 \r
6346 LRESULT CALLBACK\r
6347 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6348 {\r
6349   char buf[MSG_SIZ];\r
6350   HANDLE hwndCombo;\r
6351   char *p;\r
6352 \r
6353   switch (message) {\r
6354   case WM_INITDIALOG:\r
6355     /* Center the dialog */\r
6356     CenterWindow (hDlg, GetDesktopWindow());\r
6357     Translate(hDlg, DLG_Startup);\r
6358     /* Initialize the dialog items */\r
6359     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6360                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6361                   firstChessProgramNames);\r
6362     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6363                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6364                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6365     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6366     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6367       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6368     if (*appData.icsHelper != NULLCHAR) {\r
6369       char *q = QuoteForFilename(appData.icsHelper);\r
6370       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6371     }\r
6372     if (*appData.icsHost == NULLCHAR) {\r
6373       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6374       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6375     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6376       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6377       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6378     }\r
6379 \r
6380     if (appData.icsActive) {\r
6381       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6382     }\r
6383     else if (appData.noChessProgram) {\r
6384       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6385     }\r
6386     else {\r
6387       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6388     }\r
6389 \r
6390     SetStartupDialogEnables(hDlg);\r
6391     return TRUE;\r
6392 \r
6393   case WM_COMMAND:\r
6394     switch (LOWORD(wParam)) {\r
6395     case IDOK:\r
6396       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6397         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6398         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6399         p = buf;\r
6400         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6401         ParseArgs(StringGet, &p);\r
6402         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6403         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6404         p = buf;\r
6405         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6406         ParseArgs(StringGet, &p);\r
6407         SwapEngines(singleList); // ... and then make it 'second'\r
6408 \r
6409         appData.noChessProgram = FALSE;\r
6410         appData.icsActive = FALSE;\r
6411       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6412         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6413         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6414         p = buf;\r
6415         ParseArgs(StringGet, &p);\r
6416         if (appData.zippyPlay) {\r
6417           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6418           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6419           p = buf;\r
6420           ParseArgs(StringGet, &p);\r
6421         }\r
6422       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6423         appData.noChessProgram = TRUE;\r
6424         appData.icsActive = FALSE;\r
6425       } else {\r
6426         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6427                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6428         return TRUE;\r
6429       }\r
6430       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6431         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6432         p = buf;\r
6433         ParseArgs(StringGet, &p);\r
6434       }\r
6435       EndDialog(hDlg, TRUE);\r
6436       return TRUE;\r
6437 \r
6438     case IDCANCEL:\r
6439       ExitEvent(0);\r
6440       return TRUE;\r
6441 \r
6442     case IDM_HELPCONTENTS:\r
6443       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6444         MessageBox (GetFocus(),\r
6445                     _("Unable to activate help"),\r
6446                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6447       }\r
6448       break;\r
6449 \r
6450     default:\r
6451       SetStartupDialogEnables(hDlg);\r
6452       break;\r
6453     }\r
6454     break;\r
6455   }\r
6456   return FALSE;\r
6457 }\r
6458 \r
6459 /*---------------------------------------------------------------------------*\\r
6460  *\r
6461  * About box dialog functions\r
6462  *\r
6463 \*---------------------------------------------------------------------------*/\r
6464 \r
6465 /* Process messages for "About" dialog box */\r
6466 LRESULT CALLBACK\r
6467 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6468 {\r
6469   switch (message) {\r
6470   case WM_INITDIALOG: /* message: initialize dialog box */\r
6471     /* Center the dialog over the application window */\r
6472     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6473     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6474     Translate(hDlg, ABOUTBOX);\r
6475     JAWS_COPYRIGHT\r
6476     return (TRUE);\r
6477 \r
6478   case WM_COMMAND: /* message: received a command */\r
6479     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6480         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6481       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6482       return (TRUE);\r
6483     }\r
6484     break;\r
6485   }\r
6486   return (FALSE);\r
6487 }\r
6488 \r
6489 /*---------------------------------------------------------------------------*\\r
6490  *\r
6491  * Comment Dialog functions\r
6492  *\r
6493 \*---------------------------------------------------------------------------*/\r
6494 \r
6495 LRESULT CALLBACK\r
6496 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6497 {\r
6498   static HANDLE hwndText = NULL;\r
6499   int len, newSizeX, newSizeY;\r
6500   static int sizeX, sizeY;\r
6501   char *str;\r
6502   RECT rect;\r
6503   MINMAXINFO *mmi;\r
6504 \r
6505   switch (message) {\r
6506   case WM_INITDIALOG: /* message: initialize dialog box */\r
6507     /* Initialize the dialog items */\r
6508     Translate(hDlg, DLG_EditComment);\r
6509     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6510     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6511     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6512     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6513     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6514     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6515     SetWindowText(hDlg, commentTitle);\r
6516     if (editComment) {\r
6517       SetFocus(hwndText);\r
6518     } else {\r
6519       SetFocus(GetDlgItem(hDlg, IDOK));\r
6520     }\r
6521     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6522                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6523                 MAKELPARAM(FALSE, 0));\r
6524     /* Size and position the dialog */\r
6525     if (!commentDialog) {\r
6526       commentDialog = hDlg;\r
6527       GetClientRect(hDlg, &rect);\r
6528       sizeX = rect.right;\r
6529       sizeY = rect.bottom;\r
6530       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6531           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6532         WINDOWPLACEMENT wp;\r
6533         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6534         wp.length = sizeof(WINDOWPLACEMENT);\r
6535         wp.flags = 0;\r
6536         wp.showCmd = SW_SHOW;\r
6537         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6538         wp.rcNormalPosition.left = wpComment.x;\r
6539         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6540         wp.rcNormalPosition.top = wpComment.y;\r
6541         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6542         SetWindowPlacement(hDlg, &wp);\r
6543 \r
6544         GetClientRect(hDlg, &rect);\r
6545         newSizeX = rect.right;\r
6546         newSizeY = rect.bottom;\r
6547         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6548                               newSizeX, newSizeY);\r
6549         sizeX = newSizeX;\r
6550         sizeY = newSizeY;\r
6551       }\r
6552     }\r
6553     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6554     return FALSE;\r
6555 \r
6556   case WM_COMMAND: /* message: received a command */\r
6557     switch (LOWORD(wParam)) {\r
6558     case IDOK:\r
6559       if (editComment) {\r
6560         char *p, *q;\r
6561         /* Read changed options from the dialog box */\r
6562         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6563         len = GetWindowTextLength(hwndText);\r
6564         str = (char *) malloc(len + 1);\r
6565         GetWindowText(hwndText, str, len + 1);\r
6566         p = q = str;\r
6567         while (*q) {\r
6568           if (*q == '\r')\r
6569             q++;\r
6570           else\r
6571             *p++ = *q++;\r
6572         }\r
6573         *p = NULLCHAR;\r
6574         ReplaceComment(commentIndex, str);\r
6575         free(str);\r
6576       }\r
6577       CommentPopDown();\r
6578       return TRUE;\r
6579 \r
6580     case IDCANCEL:\r
6581     case OPT_CancelComment:\r
6582       CommentPopDown();\r
6583       return TRUE;\r
6584 \r
6585     case OPT_ClearComment:\r
6586       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6587       break;\r
6588 \r
6589     case OPT_EditComment:\r
6590       EditCommentEvent();\r
6591       return TRUE;\r
6592 \r
6593     default:\r
6594       break;\r
6595     }\r
6596     break;\r
6597 \r
6598   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6599         if( wParam == OPT_CommentText ) {\r
6600             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6601 \r
6602             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6603                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6604                 POINTL pt;\r
6605                 LRESULT index;\r
6606 \r
6607                 pt.x = LOWORD( lpMF->lParam );\r
6608                 pt.y = HIWORD( lpMF->lParam );\r
6609 \r
6610                 if(lpMF->msg == WM_CHAR) {\r
6611                         CHARRANGE sel;\r
6612                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6613                         index = sel.cpMin;\r
6614                 } else\r
6615                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6616 \r
6617                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6618                 len = GetWindowTextLength(hwndText);\r
6619                 str = (char *) malloc(len + 1);\r
6620                 GetWindowText(hwndText, str, len + 1);\r
6621                 ReplaceComment(commentIndex, str);\r
6622                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6623                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6624                 free(str);\r
6625 \r
6626                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6627                 lpMF->msg = WM_USER;\r
6628 \r
6629                 return TRUE;\r
6630             }\r
6631         }\r
6632         break;\r
6633 \r
6634   case WM_SIZE:\r
6635     newSizeX = LOWORD(lParam);\r
6636     newSizeY = HIWORD(lParam);\r
6637     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6638     sizeX = newSizeX;\r
6639     sizeY = newSizeY;\r
6640     break;\r
6641 \r
6642   case WM_GETMINMAXINFO:\r
6643     /* Prevent resizing window too small */\r
6644     mmi = (MINMAXINFO *) lParam;\r
6645     mmi->ptMinTrackSize.x = 100;\r
6646     mmi->ptMinTrackSize.y = 100;\r
6647     break;\r
6648   }\r
6649   return FALSE;\r
6650 }\r
6651 \r
6652 VOID\r
6653 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6654 {\r
6655   FARPROC lpProc;\r
6656   char *p, *q;\r
6657 \r
6658   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6659 \r
6660   if (str == NULL) str = "";\r
6661   p = (char *) malloc(2 * strlen(str) + 2);\r
6662   q = p;\r
6663   while (*str) {\r
6664     if (*str == '\n') *q++ = '\r';\r
6665     *q++ = *str++;\r
6666   }\r
6667   *q = NULLCHAR;\r
6668   if (commentText != NULL) free(commentText);\r
6669 \r
6670   commentIndex = index;\r
6671   commentTitle = title;\r
6672   commentText = p;\r
6673   editComment = edit;\r
6674 \r
6675   if (commentDialog) {\r
6676     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6677     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6678   } else {\r
6679     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6680     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6681                  hwndMain, (DLGPROC)lpProc);\r
6682     FreeProcInstance(lpProc);\r
6683   }\r
6684   commentUp = TRUE;\r
6685 }\r
6686 \r
6687 \r
6688 /*---------------------------------------------------------------------------*\\r
6689  *\r
6690  * Type-in move dialog functions\r
6691  * \r
6692 \*---------------------------------------------------------------------------*/\r
6693 \r
6694 LRESULT CALLBACK\r
6695 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6696 {\r
6697   char move[MSG_SIZ];\r
6698   HWND hInput;\r
6699 \r
6700   switch (message) {\r
6701   case WM_INITDIALOG:\r
6702     move[0] = (char) lParam;\r
6703     move[1] = NULLCHAR;\r
6704     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6705     Translate(hDlg, DLG_TypeInMove);\r
6706     hInput = GetDlgItem(hDlg, OPT_Move);\r
6707     SetWindowText(hInput, move);\r
6708     SetFocus(hInput);\r
6709     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6710     return FALSE;\r
6711 \r
6712   case WM_COMMAND:\r
6713     switch (LOWORD(wParam)) {\r
6714     case IDOK:\r
6715 \r
6716       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6717       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6718       TypeInDoneEvent(move);\r
6719       EndDialog(hDlg, TRUE);\r
6720       return TRUE;\r
6721     case IDCANCEL:\r
6722       EndDialog(hDlg, FALSE);\r
6723       return TRUE;\r
6724     default:\r
6725       break;\r
6726     }\r
6727     break;\r
6728   }\r
6729   return FALSE;\r
6730 }\r
6731 \r
6732 VOID\r
6733 PopUpMoveDialog(char firstchar)\r
6734 {\r
6735     FARPROC lpProc;\r
6736 \r
6737       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6738       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6739         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6740       FreeProcInstance(lpProc);\r
6741 }\r
6742 \r
6743 /*---------------------------------------------------------------------------*\\r
6744  *\r
6745  * Type-in name dialog functions\r
6746  * \r
6747 \*---------------------------------------------------------------------------*/\r
6748 \r
6749 LRESULT CALLBACK\r
6750 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6751 {\r
6752   char move[MSG_SIZ];\r
6753   HWND hInput;\r
6754 \r
6755   switch (message) {\r
6756   case WM_INITDIALOG:\r
6757     move[0] = (char) lParam;\r
6758     move[1] = NULLCHAR;\r
6759     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6760     Translate(hDlg, DLG_TypeInName);\r
6761     hInput = GetDlgItem(hDlg, OPT_Name);\r
6762     SetWindowText(hInput, move);\r
6763     SetFocus(hInput);\r
6764     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6765     return FALSE;\r
6766 \r
6767   case WM_COMMAND:\r
6768     switch (LOWORD(wParam)) {\r
6769     case IDOK:\r
6770       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6771       appData.userName = strdup(move);\r
6772       SetUserLogo(); DisplayLogos();\r
6773       SetGameInfo();\r
6774       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6775         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6776         DisplayTitle(move);\r
6777       }\r
6778 \r
6779 \r
6780       EndDialog(hDlg, TRUE);\r
6781       return TRUE;\r
6782     case IDCANCEL:\r
6783       EndDialog(hDlg, FALSE);\r
6784       return TRUE;\r
6785     default:\r
6786       break;\r
6787     }\r
6788     break;\r
6789   }\r
6790   return FALSE;\r
6791 }\r
6792 \r
6793 VOID\r
6794 PopUpNameDialog(char firstchar)\r
6795 {\r
6796     FARPROC lpProc;\r
6797     \r
6798       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6799       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6800         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6801       FreeProcInstance(lpProc);\r
6802 }\r
6803 \r
6804 /*---------------------------------------------------------------------------*\\r
6805  *\r
6806  *  Error dialogs\r
6807  * \r
6808 \*---------------------------------------------------------------------------*/\r
6809 \r
6810 /* Nonmodal error box */\r
6811 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6812                              WPARAM wParam, LPARAM lParam);\r
6813 \r
6814 VOID\r
6815 ErrorPopUp(char *title, char *content)\r
6816 {\r
6817   FARPROC lpProc;\r
6818   char *p, *q;\r
6819   BOOLEAN modal = hwndMain == NULL;\r
6820 \r
6821   p = content;\r
6822   q = errorMessage;\r
6823   while (*p) {\r
6824     if (*p == '\n') {\r
6825       if (modal) {\r
6826         *q++ = ' ';\r
6827         p++;\r
6828       } else {\r
6829         *q++ = '\r';\r
6830         *q++ = *p++;\r
6831       }\r
6832     } else {\r
6833       *q++ = *p++;\r
6834     }\r
6835   }\r
6836   *q = NULLCHAR;\r
6837   strncpy(errorTitle, title, sizeof(errorTitle));\r
6838   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6839   \r
6840   if (modal) {\r
6841     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6842   } else {\r
6843     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6844     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6845                  hwndMain, (DLGPROC)lpProc);\r
6846     FreeProcInstance(lpProc);\r
6847   }\r
6848 }\r
6849 \r
6850 VOID\r
6851 ErrorPopDown()\r
6852 {\r
6853   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6854   if (errorDialog == NULL) return;\r
6855   DestroyWindow(errorDialog);\r
6856   errorDialog = NULL;\r
6857   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6858 }\r
6859 \r
6860 LRESULT CALLBACK\r
6861 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6862 {\r
6863   RECT rChild;\r
6864 \r
6865   switch (message) {\r
6866   case WM_INITDIALOG:\r
6867     GetWindowRect(hDlg, &rChild);\r
6868 \r
6869     /*\r
6870     SetWindowPos(hDlg, NULL, rChild.left,\r
6871       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6872       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6873     */\r
6874 \r
6875     /* \r
6876         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6877         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6878         and it doesn't work when you resize the dialog.\r
6879         For now, just give it a default position.\r
6880     */\r
6881     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6882     Translate(hDlg, DLG_Error);\r
6883 \r
6884     errorDialog = hDlg;\r
6885     SetWindowText(hDlg, errorTitle);\r
6886     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6887     return FALSE;\r
6888 \r
6889   case WM_COMMAND:\r
6890     switch (LOWORD(wParam)) {\r
6891     case IDOK:\r
6892     case IDCANCEL:\r
6893       if (errorDialog == hDlg) errorDialog = NULL;\r
6894       DestroyWindow(hDlg);\r
6895       return TRUE;\r
6896 \r
6897     default:\r
6898       break;\r
6899     }\r
6900     break;\r
6901   }\r
6902   return FALSE;\r
6903 }\r
6904 \r
6905 #ifdef GOTHIC\r
6906 HWND gothicDialog = NULL;\r
6907 \r
6908 LRESULT CALLBACK\r
6909 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6910 {\r
6911   RECT rChild;\r
6912   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6913 \r
6914   switch (message) {\r
6915   case WM_INITDIALOG:\r
6916     GetWindowRect(hDlg, &rChild);\r
6917 \r
6918     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6919                                                              SWP_NOZORDER);\r
6920 \r
6921     /* \r
6922         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6923         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6924         and it doesn't work when you resize the dialog.\r
6925         For now, just give it a default position.\r
6926     */\r
6927     gothicDialog = hDlg;\r
6928     SetWindowText(hDlg, errorTitle);\r
6929     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6930     return FALSE;\r
6931 \r
6932   case WM_COMMAND:\r
6933     switch (LOWORD(wParam)) {\r
6934     case IDOK:\r
6935     case IDCANCEL:\r
6936       if (errorDialog == hDlg) errorDialog = NULL;\r
6937       DestroyWindow(hDlg);\r
6938       return TRUE;\r
6939 \r
6940     default:\r
6941       break;\r
6942     }\r
6943     break;\r
6944   }\r
6945   return FALSE;\r
6946 }\r
6947 \r
6948 VOID\r
6949 GothicPopUp(char *title, VariantClass variant)\r
6950 {\r
6951   FARPROC lpProc;\r
6952   static char *lastTitle;\r
6953 \r
6954   strncpy(errorTitle, title, sizeof(errorTitle));\r
6955   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6956 \r
6957   if(lastTitle != title && gothicDialog != NULL) {\r
6958     DestroyWindow(gothicDialog);\r
6959     gothicDialog = NULL;\r
6960   }\r
6961   if(variant != VariantNormal && gothicDialog == NULL) {\r
6962     title = lastTitle;\r
6963     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6964     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6965                  hwndMain, (DLGPROC)lpProc);\r
6966     FreeProcInstance(lpProc);\r
6967   }\r
6968 }\r
6969 #endif\r
6970 \r
6971 /*---------------------------------------------------------------------------*\\r
6972  *\r
6973  *  Ics Interaction console functions\r
6974  *\r
6975 \*---------------------------------------------------------------------------*/\r
6976 \r
6977 #define HISTORY_SIZE 64\r
6978 static char *history[HISTORY_SIZE];\r
6979 int histIn = 0, histP = 0;\r
6980 \r
6981 \r
6982 VOID\r
6983 SaveInHistory(char *cmd)\r
6984 {\r
6985   if (history[histIn] != NULL) {\r
6986     free(history[histIn]);\r
6987     history[histIn] = NULL;\r
6988   }\r
6989   if (*cmd == NULLCHAR) return;\r
6990   history[histIn] = StrSave(cmd);\r
6991   histIn = (histIn + 1) % HISTORY_SIZE;\r
6992   if (history[histIn] != NULL) {\r
6993     free(history[histIn]);\r
6994 \r
6995     history[histIn] = NULL;\r
6996   }\r
6997   histP = histIn;\r
6998 }\r
6999 \r
7000 char *\r
7001 PrevInHistory(char *cmd)\r
7002 {\r
7003   int newhp;\r
7004   if (histP == histIn) {\r
7005     if (history[histIn] != NULL) free(history[histIn]);\r
7006     history[histIn] = StrSave(cmd);\r
7007   }\r
7008   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7009   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7010   histP = newhp;\r
7011   return history[histP];\r
7012 }\r
7013 \r
7014 char *\r
7015 NextInHistory()\r
7016 {\r
7017   if (histP == histIn) return NULL;\r
7018   histP = (histP + 1) % HISTORY_SIZE;\r
7019   return history[histP];   \r
7020 }\r
7021 \r
7022 HMENU\r
7023 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7024 {\r
7025   HMENU hmenu, h;\r
7026   int i = 0;\r
7027   hmenu = LoadMenu(hInst, "TextMenu");\r
7028   h = GetSubMenu(hmenu, 0);\r
7029   while (e->item) {\r
7030     if (strcmp(e->item, "-") == 0) {\r
7031       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7032     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
7033       int flags = MF_STRING, j = 0;\r
7034       if (e->item[0] == '|') {\r
7035         flags |= MF_MENUBARBREAK;\r
7036         j++;\r
7037       }\r
7038       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
7039       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
7040     }\r
7041     e++;\r
7042     i++;\r
7043   } \r
7044   return hmenu;\r
7045 }\r
7046 \r
7047 WNDPROC consoleTextWindowProc;\r
7048 \r
7049 void\r
7050 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7051 {\r
7052   char buf[MSG_SIZ], name[MSG_SIZ];\r
7053   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7054   CHARRANGE sel;\r
7055 \r
7056   if (!getname) {\r
7057     SetWindowText(hInput, command);\r
7058     if (immediate) {\r
7059       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7060     } else {\r
7061       sel.cpMin = 999999;\r
7062       sel.cpMax = 999999;\r
7063       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7064       SetFocus(hInput);\r
7065     }\r
7066     return;\r
7067   }    \r
7068   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7069   if (sel.cpMin == sel.cpMax) {\r
7070     /* Expand to surrounding word */\r
7071     TEXTRANGE tr;\r
7072     do {\r
7073       tr.chrg.cpMax = sel.cpMin;\r
7074       tr.chrg.cpMin = --sel.cpMin;\r
7075       if (sel.cpMin < 0) break;\r
7076       tr.lpstrText = name;\r
7077       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7078     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7079     sel.cpMin++;\r
7080 \r
7081     do {\r
7082       tr.chrg.cpMin = sel.cpMax;\r
7083       tr.chrg.cpMax = ++sel.cpMax;\r
7084       tr.lpstrText = name;\r
7085       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7086     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7087     sel.cpMax--;\r
7088 \r
7089     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7090       MessageBeep(MB_ICONEXCLAMATION);\r
7091       return;\r
7092     }\r
7093     tr.chrg = sel;\r
7094     tr.lpstrText = name;\r
7095     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7096   } else {\r
7097     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7098       MessageBeep(MB_ICONEXCLAMATION);\r
7099       return;\r
7100     }\r
7101     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7102   }\r
7103   if (immediate) {\r
7104     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
7105     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
7106     SetWindowText(hInput, buf);\r
7107     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7108   } else {\r
7109     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
7110       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
7111     SetWindowText(hInput, buf);\r
7112     sel.cpMin = 999999;\r
7113     sel.cpMax = 999999;\r
7114     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7115     SetFocus(hInput);\r
7116   }\r
7117 }\r
7118 \r
7119 LRESULT CALLBACK \r
7120 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7121 {\r
7122   HWND hInput;\r
7123   CHARRANGE sel;\r
7124 \r
7125   switch (message) {\r
7126   case WM_KEYDOWN:\r
7127     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7128     if(wParam=='R') return 0;\r
7129     switch (wParam) {\r
7130     case VK_PRIOR:\r
7131       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7132       return 0;\r
7133     case VK_NEXT:\r
7134       sel.cpMin = 999999;\r
7135       sel.cpMax = 999999;\r
7136       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7137       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7138       return 0;\r
7139     }\r
7140     break;\r
7141   case WM_CHAR:\r
7142    if(wParam != '\022') {\r
7143     if (wParam == '\t') {\r
7144       if (GetKeyState(VK_SHIFT) < 0) {\r
7145         /* shifted */\r
7146         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7147         if (buttonDesc[0].hwnd) {\r
7148           SetFocus(buttonDesc[0].hwnd);\r
7149         } else {\r
7150           SetFocus(hwndMain);\r
7151         }\r
7152       } else {\r
7153         /* unshifted */\r
7154         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7155       }\r
7156     } else {\r
7157       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7158       JAWS_DELETE( SetFocus(hInput); )\r
7159       SendMessage(hInput, message, wParam, lParam);\r
7160     }\r
7161     return 0;\r
7162    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
7163    lParam = -1;\r
7164   case WM_RBUTTONDOWN:\r
7165     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7166       /* Move selection here if it was empty */\r
7167       POINT pt;\r
7168       pt.x = LOWORD(lParam);\r
7169       pt.y = HIWORD(lParam);\r
7170       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7171       if (sel.cpMin == sel.cpMax) {\r
7172         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7173         sel.cpMax = sel.cpMin;\r
7174         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7175       }\r
7176       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7177 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
7178       POINT pt;\r
7179       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7180       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7181       if (sel.cpMin == sel.cpMax) {\r
7182         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7183         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7184       }\r
7185       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7186         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7187       }\r
7188       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
7189       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
7190       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
7191       MenuPopup(hwnd, pt, hmenu, -1);\r
7192 }\r
7193     }\r
7194     return 0;\r
7195   case WM_RBUTTONUP:\r
7196     if (GetKeyState(VK_SHIFT) & ~1) {\r
7197       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7198         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7199     }\r
7200     return 0;\r
7201   case WM_PASTE:\r
7202     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7203     SetFocus(hInput);\r
7204     return SendMessage(hInput, message, wParam, lParam);\r
7205   case WM_MBUTTONDOWN:\r
7206     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7207   case WM_COMMAND:\r
7208     switch (LOWORD(wParam)) {\r
7209     case IDM_QuickPaste:\r
7210       {\r
7211         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7212         if (sel.cpMin == sel.cpMax) {\r
7213           MessageBeep(MB_ICONEXCLAMATION);\r
7214           return 0;\r
7215         }\r
7216         SendMessage(hwnd, WM_COPY, 0, 0);\r
7217         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7218         SendMessage(hInput, WM_PASTE, 0, 0);\r
7219         SetFocus(hInput);\r
7220         return 0;\r
7221       }\r
7222     case IDM_Cut:\r
7223       SendMessage(hwnd, WM_CUT, 0, 0);\r
7224       return 0;\r
7225     case IDM_Paste:\r
7226       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7227       return 0;\r
7228     case IDM_Copy:\r
7229       SendMessage(hwnd, WM_COPY, 0, 0);\r
7230       return 0;\r
7231     default:\r
7232       {\r
7233         int i = LOWORD(wParam) - IDM_CommandX;\r
7234         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7235             icsTextMenuEntry[i].command != NULL) {\r
7236           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7237                    icsTextMenuEntry[i].getname,\r
7238                    icsTextMenuEntry[i].immediate);\r
7239           return 0;\r
7240         }\r
7241       }\r
7242       break;\r
7243     }\r
7244     break;\r
7245   }\r
7246   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7247 }\r
7248 \r
7249 WNDPROC consoleInputWindowProc;\r
7250 \r
7251 LRESULT CALLBACK\r
7252 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7253 {\r
7254   char buf[MSG_SIZ];\r
7255   char *p;\r
7256   static BOOL sendNextChar = FALSE;\r
7257   static BOOL quoteNextChar = FALSE;\r
7258   InputSource *is = consoleInputSource;\r
7259   CHARFORMAT cf;\r
7260   CHARRANGE sel;\r
7261 \r
7262   switch (message) {\r
7263   case WM_CHAR:\r
7264     if (!appData.localLineEditing || sendNextChar) {\r
7265       is->buf[0] = (CHAR) wParam;\r
7266       is->count = 1;\r
7267       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7268       sendNextChar = FALSE;\r
7269       return 0;\r
7270     }\r
7271     if (quoteNextChar) {\r
7272       buf[0] = (char) wParam;\r
7273       buf[1] = NULLCHAR;\r
7274       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7275       quoteNextChar = FALSE;\r
7276       return 0;\r
7277     }\r
7278     switch (wParam) {\r
7279     case '\r':   /* Enter key */\r
7280       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7281       if (consoleEcho) SaveInHistory(is->buf);\r
7282       is->buf[is->count++] = '\n';\r
7283       is->buf[is->count] = NULLCHAR;\r
7284       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7285       if (consoleEcho) {\r
7286         ConsoleOutput(is->buf, is->count, TRUE);\r
7287       } else if (appData.localLineEditing) {\r
7288         ConsoleOutput("\n", 1, TRUE);\r
7289       }\r
7290       /* fall thru */\r
7291     case '\033': /* Escape key */\r
7292       SetWindowText(hwnd, "");\r
7293       cf.cbSize = sizeof(CHARFORMAT);\r
7294       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7295       if (consoleEcho) {\r
7296         cf.crTextColor = textAttribs[ColorNormal].color;\r
7297       } else {\r
7298         cf.crTextColor = COLOR_ECHOOFF;\r
7299       }\r
7300       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7301       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7302       return 0;\r
7303     case '\t':   /* Tab key */\r
7304       if (GetKeyState(VK_SHIFT) < 0) {\r
7305         /* shifted */\r
7306         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7307       } else {\r
7308         /* unshifted */\r
7309         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7310         if (buttonDesc[0].hwnd) {\r
7311           SetFocus(buttonDesc[0].hwnd);\r
7312         } else {\r
7313           SetFocus(hwndMain);\r
7314         }\r
7315       }\r
7316       return 0;\r
7317     case '\023': /* Ctrl+S */\r
7318       sendNextChar = TRUE;\r
7319       return 0;\r
7320     case '\021': /* Ctrl+Q */\r
7321       quoteNextChar = TRUE;\r
7322       return 0;\r
7323     JAWS_REPLAY\r
7324     default:\r
7325       break;\r
7326     }\r
7327     break;\r
7328   case WM_KEYDOWN:\r
7329     switch (wParam) {\r
7330     case VK_UP:\r
7331       GetWindowText(hwnd, buf, MSG_SIZ);\r
7332       p = PrevInHistory(buf);\r
7333       if (p != NULL) {\r
7334         SetWindowText(hwnd, p);\r
7335         sel.cpMin = 999999;\r
7336         sel.cpMax = 999999;\r
7337         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7338         return 0;\r
7339       }\r
7340       break;\r
7341     case VK_DOWN:\r
7342       p = NextInHistory();\r
7343       if (p != NULL) {\r
7344         SetWindowText(hwnd, p);\r
7345         sel.cpMin = 999999;\r
7346         sel.cpMax = 999999;\r
7347         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7348         return 0;\r
7349       }\r
7350       break;\r
7351     case VK_HOME:\r
7352     case VK_END:\r
7353       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7354       /* fall thru */\r
7355     case VK_PRIOR:\r
7356     case VK_NEXT:\r
7357       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7358       return 0;\r
7359     }\r
7360     break;\r
7361   case WM_MBUTTONDOWN:\r
7362     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7363       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7364     break;\r
7365   case WM_RBUTTONUP:\r
7366     if (GetKeyState(VK_SHIFT) & ~1) {\r
7367       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7368         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7369     } else {\r
7370       POINT pt;\r
7371       HMENU hmenu;\r
7372       hmenu = LoadMenu(hInst, "InputMenu");\r
7373       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7374       if (sel.cpMin == sel.cpMax) {\r
7375         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7376         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7377       }\r
7378       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7379         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7380       }\r
7381       pt.x = LOWORD(lParam);\r
7382       pt.y = HIWORD(lParam);\r
7383       MenuPopup(hwnd, pt, hmenu, -1);\r
7384     }\r
7385     return 0;\r
7386   case WM_COMMAND:\r
7387     switch (LOWORD(wParam)) { \r
7388     case IDM_Undo:\r
7389       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7390       return 0;\r
7391     case IDM_SelectAll:\r
7392       sel.cpMin = 0;\r
7393       sel.cpMax = -1; /*999999?*/\r
7394       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7395       return 0;\r
7396     case IDM_Cut:\r
7397       SendMessage(hwnd, WM_CUT, 0, 0);\r
7398       return 0;\r
7399     case IDM_Paste:\r
7400       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7401       return 0;\r
7402     case IDM_Copy:\r
7403       SendMessage(hwnd, WM_COPY, 0, 0);\r
7404       return 0;\r
7405     }\r
7406     break;\r
7407   }\r
7408   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7409 }\r
7410 \r
7411 #define CO_MAX  100000\r
7412 #define CO_TRIM   1000\r
7413 \r
7414 LRESULT CALLBACK\r
7415 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7416 {\r
7417   static SnapData sd;\r
7418   HWND hText, hInput;\r
7419   RECT rect;\r
7420   static int sizeX, sizeY;\r
7421   int newSizeX, newSizeY;\r
7422   MINMAXINFO *mmi;\r
7423   WORD wMask;\r
7424 \r
7425   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7426   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7427 \r
7428   switch (message) {\r
7429   case WM_NOTIFY:\r
7430     if (((NMHDR*)lParam)->code == EN_LINK)\r
7431     {\r
7432       ENLINK *pLink = (ENLINK*)lParam;\r
7433       if (pLink->msg == WM_LBUTTONUP)\r
7434       {\r
7435         TEXTRANGE tr;\r
7436 \r
7437         tr.chrg = pLink->chrg;\r
7438         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7439         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7440         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7441         free(tr.lpstrText);\r
7442       }\r
7443     }\r
7444     break;\r
7445   case WM_INITDIALOG: /* message: initialize dialog box */\r
7446     hwndConsole = hDlg;\r
7447     SetFocus(hInput);\r
7448     consoleTextWindowProc = (WNDPROC)\r
7449       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7450     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7451     consoleInputWindowProc = (WNDPROC)\r
7452       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7453     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7454     Colorize(ColorNormal, TRUE);\r
7455     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7456     ChangedConsoleFont();\r
7457     GetClientRect(hDlg, &rect);\r
7458     sizeX = rect.right;\r
7459     sizeY = rect.bottom;\r
7460     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7461         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7462       WINDOWPLACEMENT wp;\r
7463       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7464       wp.length = sizeof(WINDOWPLACEMENT);\r
7465       wp.flags = 0;\r
7466       wp.showCmd = SW_SHOW;\r
7467       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7468       wp.rcNormalPosition.left = wpConsole.x;\r
7469       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7470       wp.rcNormalPosition.top = wpConsole.y;\r
7471       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7472       SetWindowPlacement(hDlg, &wp);\r
7473     }\r
7474 \r
7475    // [HGM] Chessknight's change 2004-07-13\r
7476    else { /* Determine Defaults */\r
7477        WINDOWPLACEMENT wp;\r
7478        wpConsole.x = wpMain.width + 1;\r
7479        wpConsole.y = wpMain.y;\r
7480        wpConsole.width = screenWidth -  wpMain.width;\r
7481        wpConsole.height = wpMain.height;\r
7482        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7483        wp.length = sizeof(WINDOWPLACEMENT);\r
7484        wp.flags = 0;\r
7485        wp.showCmd = SW_SHOW;\r
7486        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7487        wp.rcNormalPosition.left = wpConsole.x;\r
7488        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7489        wp.rcNormalPosition.top = wpConsole.y;\r
7490        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7491        SetWindowPlacement(hDlg, &wp);\r
7492     }\r
7493 \r
7494    // Allow hText to highlight URLs and send notifications on them\r
7495    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7496    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7497    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7498    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7499 \r
7500     return FALSE;\r
7501 \r
7502   case WM_SETFOCUS:\r
7503     SetFocus(hInput);\r
7504     return 0;\r
7505 \r
7506   case WM_CLOSE:\r
7507     ExitEvent(0);\r
7508     /* not reached */\r
7509     break;\r
7510 \r
7511   case WM_SIZE:\r
7512     if (IsIconic(hDlg)) break;\r
7513     newSizeX = LOWORD(lParam);\r
7514     newSizeY = HIWORD(lParam);\r
7515     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7516       RECT rectText, rectInput;\r
7517       POINT pt;\r
7518       int newTextHeight, newTextWidth;\r
7519       GetWindowRect(hText, &rectText);\r
7520       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7521       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7522       if (newTextHeight < 0) {\r
7523         newSizeY += -newTextHeight;\r
7524         newTextHeight = 0;\r
7525       }\r
7526       SetWindowPos(hText, NULL, 0, 0,\r
7527         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7528       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7529       pt.x = rectInput.left;\r
7530       pt.y = rectInput.top + newSizeY - sizeY;\r
7531       ScreenToClient(hDlg, &pt);\r
7532       SetWindowPos(hInput, NULL, \r
7533         pt.x, pt.y, /* needs client coords */   \r
7534         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7535         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7536     }\r
7537     sizeX = newSizeX;\r
7538     sizeY = newSizeY;\r
7539     break;\r
7540 \r
7541   case WM_GETMINMAXINFO:\r
7542     /* Prevent resizing window too small */\r
7543     mmi = (MINMAXINFO *) lParam;\r
7544     mmi->ptMinTrackSize.x = 100;\r
7545     mmi->ptMinTrackSize.y = 100;\r
7546     break;\r
7547 \r
7548   /* [AS] Snapping */\r
7549   case WM_ENTERSIZEMOVE:\r
7550     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7551 \r
7552   case WM_SIZING:\r
7553     return OnSizing( &sd, hDlg, wParam, lParam );\r
7554 \r
7555   case WM_MOVING:\r
7556     return OnMoving( &sd, hDlg, wParam, lParam );\r
7557 \r
7558   case WM_EXITSIZEMOVE:\r
7559         UpdateICSWidth(hText);\r
7560     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7561   }\r
7562 \r
7563   return DefWindowProc(hDlg, message, wParam, lParam);\r
7564 }\r
7565 \r
7566 \r
7567 VOID\r
7568 ConsoleCreate()\r
7569 {\r
7570   HWND hCons;\r
7571   if (hwndConsole) return;\r
7572   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7573   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7574 }\r
7575 \r
7576 \r
7577 VOID\r
7578 ConsoleOutput(char* data, int length, int forceVisible)\r
7579 {\r
7580   HWND hText;\r
7581   int trim, exlen;\r
7582   char *p, *q;\r
7583   char buf[CO_MAX+1];\r
7584   POINT pEnd;\r
7585   RECT rect;\r
7586   static int delayLF = 0;\r
7587   CHARRANGE savesel, sel;\r
7588 \r
7589   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7590   p = data;\r
7591   q = buf;\r
7592   if (delayLF) {\r
7593     *q++ = '\r';\r
7594     *q++ = '\n';\r
7595     delayLF = 0;\r
7596   }\r
7597   while (length--) {\r
7598     if (*p == '\n') {\r
7599       if (*++p) {\r
7600         *q++ = '\r';\r
7601         *q++ = '\n';\r
7602       } else {\r
7603         delayLF = 1;\r
7604       }\r
7605     } else if (*p == '\007') {\r
7606        MyPlaySound(&sounds[(int)SoundBell]);\r
7607        p++;\r
7608     } else {\r
7609       *q++ = *p++;\r
7610     }\r
7611   }\r
7612   *q = NULLCHAR;\r
7613   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7614   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7615   /* Save current selection */\r
7616   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7617   exlen = GetWindowTextLength(hText);\r
7618   /* Find out whether current end of text is visible */\r
7619   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7620   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7621   /* Trim existing text if it's too long */\r
7622   if (exlen + (q - buf) > CO_MAX) {\r
7623     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7624     sel.cpMin = 0;\r
7625     sel.cpMax = trim;\r
7626     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7627     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7628     exlen -= trim;\r
7629     savesel.cpMin -= trim;\r
7630     savesel.cpMax -= trim;\r
7631     if (exlen < 0) exlen = 0;\r
7632     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7633     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7634   }\r
7635   /* Append the new text */\r
7636   sel.cpMin = exlen;\r
7637   sel.cpMax = exlen;\r
7638   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7639   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7640   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7641   if (forceVisible || exlen == 0 ||\r
7642       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7643        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7644     /* Scroll to make new end of text visible if old end of text\r
7645        was visible or new text is an echo of user typein */\r
7646     sel.cpMin = 9999999;\r
7647     sel.cpMax = 9999999;\r
7648     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7649     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7650     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7651     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7652   }\r
7653   if (savesel.cpMax == exlen || forceVisible) {\r
7654     /* Move insert point to new end of text if it was at the old\r
7655        end of text or if the new text is an echo of user typein */\r
7656     sel.cpMin = 9999999;\r
7657     sel.cpMax = 9999999;\r
7658     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7659   } else {\r
7660     /* Restore previous selection */\r
7661     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7662   }\r
7663   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7664 }\r
7665 \r
7666 /*---------*/\r
7667 \r
7668 \r
7669 void\r
7670 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7671 {\r
7672   char buf[100];\r
7673   char *str;\r
7674   COLORREF oldFg, oldBg;\r
7675   HFONT oldFont;\r
7676   RECT rect;\r
7677 \r
7678   if(copyNumber > 1)\r
7679     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7680 \r
7681   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7682   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7683   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7684 \r
7685   rect.left = x;\r
7686   rect.right = x + squareSize;\r
7687   rect.top  = y;\r
7688   rect.bottom = y + squareSize;\r
7689   str = buf;\r
7690 \r
7691   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7692                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7693              y, ETO_CLIPPED|ETO_OPAQUE,\r
7694              &rect, str, strlen(str), NULL);\r
7695 \r
7696   (void) SetTextColor(hdc, oldFg);\r
7697   (void) SetBkColor(hdc, oldBg);\r
7698   (void) SelectObject(hdc, oldFont);\r
7699 }\r
7700 \r
7701 void\r
7702 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7703               RECT *rect, char *color, char *flagFell)\r
7704 {\r
7705   char buf[100];\r
7706   char *str;\r
7707   COLORREF oldFg, oldBg;\r
7708   HFONT oldFont;\r
7709 \r
7710   if (twoBoards && partnerUp) return;\r
7711   if (appData.clockMode) {\r
7712     if (tinyLayout == 2)\r
7713       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7714     else\r
7715       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7716     str = buf;\r
7717   } else {\r
7718     str = color;\r
7719   }\r
7720 \r
7721   if (highlight) {\r
7722     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7723     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7724   } else {\r
7725     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7726     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7727   }\r
7728 \r
7729   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7730 \r
7731   JAWS_SILENCE\r
7732 \r
7733   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7734              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7735              rect, str, strlen(str), NULL);\r
7736   if(logoHeight > 0 && appData.clockMode) {\r
7737       RECT r;\r
7738       str += strlen(color)+2;\r
7739       r.top = rect->top + logoHeight/2;\r
7740       r.left = rect->left;\r
7741       r.right = rect->right;\r
7742       r.bottom = rect->bottom;\r
7743       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7744                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7745                  &r, str, strlen(str), NULL);\r
7746   }\r
7747   (void) SetTextColor(hdc, oldFg);\r
7748   (void) SetBkColor(hdc, oldBg);\r
7749   (void) SelectObject(hdc, oldFont);\r
7750 }\r
7751 \r
7752 \r
7753 int\r
7754 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7755            OVERLAPPED *ovl)\r
7756 {\r
7757   int ok, err;\r
7758 \r
7759   /* [AS]  */\r
7760   if( count <= 0 ) {\r
7761     if (appData.debugMode) {\r
7762       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7763     }\r
7764 \r
7765     return ERROR_INVALID_USER_BUFFER;\r
7766   }\r
7767 \r
7768   ResetEvent(ovl->hEvent);\r
7769   ovl->Offset = ovl->OffsetHigh = 0;\r
7770   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7771   if (ok) {\r
7772     err = NO_ERROR;\r
7773   } else {\r
7774     err = GetLastError();\r
7775     if (err == ERROR_IO_PENDING) {\r
7776       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7777       if (ok)\r
7778         err = NO_ERROR;\r
7779       else\r
7780         err = GetLastError();\r
7781     }\r
7782   }\r
7783   return err;\r
7784 }\r
7785 \r
7786 int\r
7787 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7788             OVERLAPPED *ovl)\r
7789 {\r
7790   int ok, err;\r
7791 \r
7792   ResetEvent(ovl->hEvent);\r
7793   ovl->Offset = ovl->OffsetHigh = 0;\r
7794   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7795   if (ok) {\r
7796     err = NO_ERROR;\r
7797   } else {\r
7798     err = GetLastError();\r
7799     if (err == ERROR_IO_PENDING) {\r
7800       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7801       if (ok)\r
7802         err = NO_ERROR;\r
7803       else\r
7804         err = GetLastError();\r
7805     }\r
7806 \r
7807   }\r
7808   return err;\r
7809 }\r
7810 \r
7811 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7812 void CheckForInputBufferFull( InputSource * is )\r
7813 {\r
7814     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7815         /* Look for end of line */\r
7816         char * p = is->buf;\r
7817         \r
7818         while( p < is->next && *p != '\n' ) {\r
7819             p++;\r
7820         }\r
7821 \r
7822         if( p >= is->next ) {\r
7823             if (appData.debugMode) {\r
7824                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7825             }\r
7826 \r
7827             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7828             is->count = (DWORD) -1;\r
7829             is->next = is->buf;\r
7830         }\r
7831     }\r
7832 }\r
7833 \r
7834 DWORD\r
7835 InputThread(LPVOID arg)\r
7836 {\r
7837   InputSource *is;\r
7838   OVERLAPPED ovl;\r
7839 \r
7840   is = (InputSource *) arg;\r
7841   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7842   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7843   while (is->hThread != NULL) {\r
7844     is->error = DoReadFile(is->hFile, is->next,\r
7845                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7846                            &is->count, &ovl);\r
7847     if (is->error == NO_ERROR) {\r
7848       is->next += is->count;\r
7849     } else {\r
7850       if (is->error == ERROR_BROKEN_PIPE) {\r
7851         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7852         is->count = 0;\r
7853       } else {\r
7854         is->count = (DWORD) -1;\r
7855         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7856         break; \r
7857       }\r
7858     }\r
7859 \r
7860     CheckForInputBufferFull( is );\r
7861 \r
7862     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7863 \r
7864     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7865 \r
7866     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7867   }\r
7868 \r
7869   CloseHandle(ovl.hEvent);\r
7870   CloseHandle(is->hFile);\r
7871 \r
7872   if (appData.debugMode) {\r
7873     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7874   }\r
7875 \r
7876   return 0;\r
7877 }\r
7878 \r
7879 \r
7880 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7881 DWORD\r
7882 NonOvlInputThread(LPVOID arg)\r
7883 {\r
7884   InputSource *is;\r
7885   char *p, *q;\r
7886   int i;\r
7887   char prev;\r
7888 \r
7889   is = (InputSource *) arg;\r
7890   while (is->hThread != NULL) {\r
7891     is->error = ReadFile(is->hFile, is->next,\r
7892                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7893                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7894     if (is->error == NO_ERROR) {\r
7895       /* Change CRLF to LF */\r
7896       if (is->next > is->buf) {\r
7897         p = is->next - 1;\r
7898         i = is->count + 1;\r
7899       } else {\r
7900         p = is->next;\r
7901         i = is->count;\r
7902       }\r
7903       q = p;\r
7904       prev = NULLCHAR;\r
7905       while (i > 0) {\r
7906         if (prev == '\r' && *p == '\n') {\r
7907           *(q-1) = '\n';\r
7908           is->count--;\r
7909         } else { \r
7910           *q++ = *p;\r
7911         }\r
7912         prev = *p++;\r
7913         i--;\r
7914       }\r
7915       *q = NULLCHAR;\r
7916       is->next = q;\r
7917     } else {\r
7918       if (is->error == ERROR_BROKEN_PIPE) {\r
7919         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7920         is->count = 0; \r
7921       } else {\r
7922         is->count = (DWORD) -1;\r
7923       }\r
7924     }\r
7925 \r
7926     CheckForInputBufferFull( is );\r
7927 \r
7928     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7929 \r
7930     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7931 \r
7932     if (is->count < 0) break;  /* Quit on error */\r
7933   }\r
7934   CloseHandle(is->hFile);\r
7935   return 0;\r
7936 }\r
7937 \r
7938 DWORD\r
7939 SocketInputThread(LPVOID arg)\r
7940 {\r
7941   InputSource *is;\r
7942 \r
7943   is = (InputSource *) arg;\r
7944   while (is->hThread != NULL) {\r
7945     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7946     if ((int)is->count == SOCKET_ERROR) {\r
7947       is->count = (DWORD) -1;\r
7948       is->error = WSAGetLastError();\r
7949     } else {\r
7950       is->error = NO_ERROR;\r
7951       is->next += is->count;\r
7952       if (is->count == 0 && is->second == is) {\r
7953         /* End of file on stderr; quit with no message */\r
7954         break;\r
7955       }\r
7956     }\r
7957     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7958 \r
7959     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7960 \r
7961     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7962   }\r
7963   return 0;\r
7964 }\r
7965 \r
7966 VOID\r
7967 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7968 {\r
7969   InputSource *is;\r
7970 \r
7971   is = (InputSource *) lParam;\r
7972   if (is->lineByLine) {\r
7973     /* Feed in lines one by one */\r
7974     char *p = is->buf;\r
7975     char *q = p;\r
7976     while (q < is->next) {\r
7977       if (*q++ == '\n') {\r
7978         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7979         p = q;\r
7980       }\r
7981     }\r
7982     \r
7983     /* Move any partial line to the start of the buffer */\r
7984     q = is->buf;\r
7985     while (p < is->next) {\r
7986       *q++ = *p++;\r
7987     }\r
7988     is->next = q;\r
7989 \r
7990     if (is->error != NO_ERROR || is->count == 0) {\r
7991       /* Notify backend of the error.  Note: If there was a partial\r
7992          line at the end, it is not flushed through. */\r
7993       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7994     }\r
7995   } else {\r
7996     /* Feed in the whole chunk of input at once */\r
7997     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7998     is->next = is->buf;\r
7999   }\r
8000 }\r
8001 \r
8002 /*---------------------------------------------------------------------------*\\r
8003  *\r
8004  *  Menu enables. Used when setting various modes.\r
8005  *\r
8006 \*---------------------------------------------------------------------------*/\r
8007 \r
8008 typedef struct {\r
8009   int item;\r
8010   int flags;\r
8011 } Enables;\r
8012 \r
8013 VOID\r
8014 GreyRevert(Boolean grey)\r
8015 { // [HGM] vari: for retracting variations in local mode\r
8016   HMENU hmenu = GetMenu(hwndMain);\r
8017   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
8018   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
8019 }\r
8020 \r
8021 VOID\r
8022 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8023 {\r
8024   while (enab->item > 0) {\r
8025     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8026     enab++;\r
8027   }\r
8028 }\r
8029 \r
8030 Enables gnuEnables[] = {\r
8031   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8032   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8033   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8034   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8035   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8036   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8037   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8038   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8039   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8040   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
8041   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8042   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8043   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8044 \r
8045 \r
8046   // Needed to switch from ncp to GNU mode on Engine Load\r
8047   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8048   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8049   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8050   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8051   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8052   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8053   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
8054   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
8055   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
8056   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
8057   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8058   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8059   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8060   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8061   { -1, -1 }\r
8062 };\r
8063 \r
8064 Enables icsEnables[] = {\r
8065   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8066   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8067   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8068   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8069   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8070   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8071   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8072   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8073   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8074   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8075   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8076   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8077   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8078   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
8079   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
8080   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8081   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8082   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8083   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8084   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
8085   { -1, -1 }\r
8086 };\r
8087 \r
8088 #if ZIPPY\r
8089 Enables zippyEnables[] = {\r
8090   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8091   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8092   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8093   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
8094   { -1, -1 }\r
8095 };\r
8096 #endif\r
8097 \r
8098 Enables ncpEnables[] = {\r
8099   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8100   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8101   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8102   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8103   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8104   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8105   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8106   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8107   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8108   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8109   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8110   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8111   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8112   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8113   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8114   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8115   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8116   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8117   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8118   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8119   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8120   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
8121   { -1, -1 }\r
8122 };\r
8123 \r
8124 Enables trainingOnEnables[] = {\r
8125   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8126   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
8127   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8128   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8129   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8130   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8131   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8132   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8133   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8134   { -1, -1 }\r
8135 };\r
8136 \r
8137 Enables trainingOffEnables[] = {\r
8138   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8139   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
8140   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8141   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8142   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8143   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8144   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8145   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8146   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8147   { -1, -1 }\r
8148 };\r
8149 \r
8150 /* These modify either ncpEnables or gnuEnables */\r
8151 Enables cmailEnables[] = {\r
8152   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8153   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8154   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8155   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8156   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8157   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8158   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8159   { -1, -1 }\r
8160 };\r
8161 \r
8162 Enables machineThinkingEnables[] = {\r
8163   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8164   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8165   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8166   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8167   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8168   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8169   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8170   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8171   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8172   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8173   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8174   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8175   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8176 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8177   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8178   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8179   { -1, -1 }\r
8180 };\r
8181 \r
8182 Enables userThinkingEnables[] = {\r
8183   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8184   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8185   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8186   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8187   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8188   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8189   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8190   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8191   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8192   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8193   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8194   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8195   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8196 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8197   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8198   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8199   { -1, -1 }\r
8200 };\r
8201 \r
8202 /*---------------------------------------------------------------------------*\\r
8203  *\r
8204  *  Front-end interface functions exported by XBoard.\r
8205  *  Functions appear in same order as prototypes in frontend.h.\r
8206  * \r
8207 \*---------------------------------------------------------------------------*/\r
8208 VOID\r
8209 CheckMark(UINT item, int state)\r
8210 {\r
8211     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
8212 }\r
8213 \r
8214 VOID\r
8215 ModeHighlight()\r
8216 {\r
8217   static UINT prevChecked = 0;\r
8218   static int prevPausing = 0;\r
8219   UINT nowChecked;\r
8220 \r
8221   if (pausing != prevPausing) {\r
8222     prevPausing = pausing;\r
8223     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8224                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8225     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8226   }\r
8227 \r
8228   switch (gameMode) {\r
8229   case BeginningOfGame:\r
8230     if (appData.icsActive)\r
8231       nowChecked = IDM_IcsClient;\r
8232     else if (appData.noChessProgram)\r
8233       nowChecked = IDM_EditGame;\r
8234     else\r
8235       nowChecked = IDM_MachineBlack;\r
8236     break;\r
8237   case MachinePlaysBlack:\r
8238     nowChecked = IDM_MachineBlack;\r
8239     break;\r
8240   case MachinePlaysWhite:\r
8241     nowChecked = IDM_MachineWhite;\r
8242     break;\r
8243   case TwoMachinesPlay:\r
8244     nowChecked = IDM_TwoMachines;\r
8245     break;\r
8246   case AnalyzeMode:\r
8247     nowChecked = IDM_AnalysisMode;\r
8248     break;\r
8249   case AnalyzeFile:\r
8250     nowChecked = IDM_AnalyzeFile;\r
8251     break;\r
8252   case EditGame:\r
8253     nowChecked = IDM_EditGame;\r
8254     break;\r
8255   case PlayFromGameFile:\r
8256     nowChecked = IDM_LoadGame;\r
8257     break;\r
8258   case EditPosition:\r
8259     nowChecked = IDM_EditPosition;\r
8260     break;\r
8261   case Training:\r
8262     nowChecked = IDM_Training;\r
8263     break;\r
8264   case IcsPlayingWhite:\r
8265   case IcsPlayingBlack:\r
8266   case IcsObserving:\r
8267   case IcsIdle:\r
8268     nowChecked = IDM_IcsClient;\r
8269     break;\r
8270   default:\r
8271   case EndOfGame:\r
8272     nowChecked = 0;\r
8273     break;\r
8274   }\r
8275   if(prevChecked == IDM_TwoMachines) // [HGM] 'Machine Match' might have gotten disabled when stopping match\r
8276     EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_ENABLED);\r
8277   CheckMark(prevChecked, MF_UNCHECKED);\r
8278   CheckMark(nowChecked, MF_CHECKED);\r
8279   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8280 \r
8281   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8282     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8283                           MF_BYCOMMAND|MF_ENABLED);\r
8284   } else {\r
8285     (void) EnableMenuItem(GetMenu(hwndMain), \r
8286                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8287   }\r
8288 \r
8289   prevChecked = nowChecked;\r
8290 \r
8291   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8292   if (appData.icsActive) {\r
8293        if (appData.icsEngineAnalyze) {\r
8294                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8295        } else {\r
8296                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8297        }\r
8298   }\r
8299   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8300 }\r
8301 \r
8302 VOID\r
8303 SetICSMode()\r
8304 {\r
8305   HMENU hmenu = GetMenu(hwndMain);\r
8306   SetMenuEnables(hmenu, icsEnables);\r
8307   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8308     MF_BYCOMMAND|MF_ENABLED);\r
8309 #if ZIPPY\r
8310   if (appData.zippyPlay) {\r
8311     SetMenuEnables(hmenu, zippyEnables);\r
8312     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8313          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8314           MF_BYCOMMAND|MF_ENABLED);\r
8315   }\r
8316 #endif\r
8317 }\r
8318 \r
8319 VOID\r
8320 SetGNUMode()\r
8321 {\r
8322   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8323 }\r
8324 \r
8325 VOID\r
8326 SetNCPMode()\r
8327 {\r
8328   HMENU hmenu = GetMenu(hwndMain);\r
8329   SetMenuEnables(hmenu, ncpEnables);\r
8330     DrawMenuBar(hwndMain);\r
8331 }\r
8332 \r
8333 VOID\r
8334 SetCmailMode()\r
8335 {\r
8336   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8337 }\r
8338 \r
8339 VOID \r
8340 SetTrainingModeOn()\r
8341 {\r
8342   int i;\r
8343   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8344   for (i = 0; i < N_BUTTONS; i++) {\r
8345     if (buttonDesc[i].hwnd != NULL)\r
8346       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8347   }\r
8348   CommentPopDown();\r
8349 }\r
8350 \r
8351 VOID SetTrainingModeOff()\r
8352 {\r
8353   int i;\r
8354   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8355   for (i = 0; i < N_BUTTONS; i++) {\r
8356     if (buttonDesc[i].hwnd != NULL)\r
8357       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8358   }\r
8359 }\r
8360 \r
8361 \r
8362 VOID\r
8363 SetUserThinkingEnables()\r
8364 {\r
8365   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8366 }\r
8367 \r
8368 VOID\r
8369 SetMachineThinkingEnables()\r
8370 {\r
8371   HMENU hMenu = GetMenu(hwndMain);\r
8372   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8373 \r
8374   SetMenuEnables(hMenu, machineThinkingEnables);\r
8375 \r
8376   if (gameMode == MachinePlaysBlack) {\r
8377     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8378   } else if (gameMode == MachinePlaysWhite) {\r
8379     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8380   } else if (gameMode == TwoMachinesPlay) {\r
8381     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8382   }\r
8383 }\r
8384 \r
8385 \r
8386 VOID\r
8387 DisplayTitle(char *str)\r
8388 {\r
8389   char title[MSG_SIZ], *host;\r
8390   if (str[0] != NULLCHAR) {\r
8391     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8392   } else if (appData.icsActive) {\r
8393     if (appData.icsCommPort[0] != NULLCHAR)\r
8394       host = "ICS";\r
8395     else \r
8396       host = appData.icsHost;\r
8397       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8398   } else if (appData.noChessProgram) {\r
8399     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8400   } else {\r
8401     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8402     strcat(title, ": ");\r
8403     strcat(title, first.tidy);\r
8404   }\r
8405   SetWindowText(hwndMain, title);\r
8406 }\r
8407 \r
8408 \r
8409 VOID\r
8410 DisplayMessage(char *str1, char *str2)\r
8411 {\r
8412   HDC hdc;\r
8413   HFONT oldFont;\r
8414   int remain = MESSAGE_TEXT_MAX - 1;\r
8415   int len;\r
8416 \r
8417   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8418   messageText[0] = NULLCHAR;\r
8419   if (*str1) {\r
8420     len = strlen(str1);\r
8421     if (len > remain) len = remain;\r
8422     strncpy(messageText, str1, len);\r
8423     messageText[len] = NULLCHAR;\r
8424     remain -= len;\r
8425   }\r
8426   if (*str2 && remain >= 2) {\r
8427     if (*str1) {\r
8428       strcat(messageText, "  ");\r
8429       remain -= 2;\r
8430     }\r
8431     len = strlen(str2);\r
8432     if (len > remain) len = remain;\r
8433     strncat(messageText, str2, len);\r
8434   }\r
8435   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8436   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8437 \r
8438   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8439 \r
8440   SAYMACHINEMOVE();\r
8441 \r
8442   hdc = GetDC(hwndMain);\r
8443   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8444   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8445              &messageRect, messageText, strlen(messageText), NULL);\r
8446   (void) SelectObject(hdc, oldFont);\r
8447   (void) ReleaseDC(hwndMain, hdc);\r
8448 }\r
8449 \r
8450 VOID\r
8451 DisplayError(char *str, int error)\r
8452 {\r
8453   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8454   int len;\r
8455 \r
8456   if (error == 0) {\r
8457     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8458   } else {\r
8459     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8460                         NULL, error, LANG_NEUTRAL,\r
8461                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8462     if (len > 0) {\r
8463       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8464     } else {\r
8465       ErrorMap *em = errmap;\r
8466       while (em->err != 0 && em->err != error) em++;\r
8467       if (em->err != 0) {\r
8468         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8469       } else {\r
8470         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8471       }\r
8472     }\r
8473   }\r
8474   \r
8475   ErrorPopUp(_("Error"), buf);\r
8476 }\r
8477 \r
8478 \r
8479 VOID\r
8480 DisplayMoveError(char *str)\r
8481 {\r
8482   fromX = fromY = -1;\r
8483   ClearHighlights();\r
8484   DrawPosition(FALSE, NULL);\r
8485   if (appData.popupMoveErrors) {\r
8486     ErrorPopUp(_("Error"), str);\r
8487   } else {\r
8488     DisplayMessage(str, "");\r
8489     moveErrorMessageUp = TRUE;\r
8490   }\r
8491 }\r
8492 \r
8493 VOID\r
8494 DisplayFatalError(char *str, int error, int exitStatus)\r
8495 {\r
8496   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8497   int len;\r
8498   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8499 \r
8500   if (error != 0) {\r
8501     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8502                         NULL, error, LANG_NEUTRAL,\r
8503                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8504     if (len > 0) {\r
8505       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8506     } else {\r
8507       ErrorMap *em = errmap;\r
8508       while (em->err != 0 && em->err != error) em++;\r
8509       if (em->err != 0) {\r
8510         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8511       } else {\r
8512         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8513       }\r
8514     }\r
8515     str = buf;\r
8516   }\r
8517   if (appData.debugMode) {\r
8518     fprintf(debugFP, "%s: %s\n", label, str);\r
8519   }\r
8520   if (appData.popupExitMessage) {\r
8521     if(appData.icsActive) SendToICS("logout\n"); // [HGM] make sure no new games will be started!\r
8522     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8523                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8524   }\r
8525   ExitEvent(exitStatus);\r
8526 }\r
8527 \r
8528 \r
8529 VOID\r
8530 DisplayInformation(char *str)\r
8531 {\r
8532   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8533 }\r
8534 \r
8535 char *\r
8536 Shorten (char *s)\r
8537 {\r
8538   return s;\r
8539 }\r
8540 \r
8541 VOID\r
8542 DisplayNote(char *str)\r
8543 {\r
8544   ErrorPopUp(_("Note"), str);\r
8545 }\r
8546 \r
8547 \r
8548 typedef struct {\r
8549   char *title, *question, *replyPrefix;\r
8550   ProcRef pr;\r
8551 } QuestionParams;\r
8552 \r
8553 LRESULT CALLBACK\r
8554 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8555 {\r
8556   static QuestionParams *qp;\r
8557   char reply[MSG_SIZ];\r
8558   int len, err;\r
8559 \r
8560   switch (message) {\r
8561   case WM_INITDIALOG:\r
8562     qp = (QuestionParams *) lParam;\r
8563     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8564     Translate(hDlg, DLG_Question);\r
8565     SetWindowText(hDlg, qp->title);\r
8566     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8567     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8568     return FALSE;\r
8569 \r
8570   case WM_COMMAND:\r
8571     switch (LOWORD(wParam)) {\r
8572     case IDOK:\r
8573       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8574       if (*reply) strcat(reply, " ");\r
8575       len = strlen(reply);\r
8576       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8577       strcat(reply, "\n");\r
8578       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8579       EndDialog(hDlg, TRUE);\r
8580       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8581       return TRUE;\r
8582     case IDCANCEL:\r
8583       EndDialog(hDlg, FALSE);\r
8584       return TRUE;\r
8585     default:\r
8586       break;\r
8587     }\r
8588     break;\r
8589   }\r
8590   return FALSE;\r
8591 }\r
8592 \r
8593 VOID\r
8594 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8595 {\r
8596     QuestionParams qp;\r
8597     FARPROC lpProc;\r
8598     \r
8599     qp.title = title;\r
8600     qp.question = question;\r
8601     qp.replyPrefix = replyPrefix;\r
8602     qp.pr = pr;\r
8603     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8604     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8605       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8606     FreeProcInstance(lpProc);\r
8607 }\r
8608 \r
8609 /* [AS] Pick FRC position */\r
8610 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8611 {\r
8612     static int * lpIndexFRC;\r
8613     BOOL index_is_ok;\r
8614     char buf[16];\r
8615 \r
8616     switch( message )\r
8617     {\r
8618     case WM_INITDIALOG:\r
8619         lpIndexFRC = (int *) lParam;\r
8620 \r
8621         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8622         Translate(hDlg, DLG_NewGameFRC);\r
8623 \r
8624         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8625         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8626         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8627         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8628 \r
8629         break;\r
8630 \r
8631     case WM_COMMAND:\r
8632         switch( LOWORD(wParam) ) {\r
8633         case IDOK:\r
8634             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8635             EndDialog( hDlg, 0 );\r
8636             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8637             return TRUE;\r
8638         case IDCANCEL:\r
8639             EndDialog( hDlg, 1 );   \r
8640             return TRUE;\r
8641         case IDC_NFG_Edit:\r
8642             if( HIWORD(wParam) == EN_CHANGE ) {\r
8643                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8644 \r
8645                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8646             }\r
8647             return TRUE;\r
8648         case IDC_NFG_Random:\r
8649           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8650             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8651             return TRUE;\r
8652         }\r
8653 \r
8654         break;\r
8655     }\r
8656 \r
8657     return FALSE;\r
8658 }\r
8659 \r
8660 int NewGameFRC()\r
8661 {\r
8662     int result;\r
8663     int index = appData.defaultFrcPosition;\r
8664     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8665 \r
8666     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8667 \r
8668     if( result == 0 ) {\r
8669         appData.defaultFrcPosition = index;\r
8670     }\r
8671 \r
8672     return result;\r
8673 }\r
8674 \r
8675 /* [AS] Game list options. Refactored by HGM */\r
8676 \r
8677 HWND gameListOptionsDialog;\r
8678 \r
8679 // low-level front-end: clear text edit / list widget\r
8680 void\r
8681 \r
8682 GLT_ClearList()\r
8683 {\r
8684     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8685 }\r
8686 \r
8687 // low-level front-end: clear text edit / list widget\r
8688 void\r
8689 GLT_DeSelectList()\r
8690 {\r
8691     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8692 }\r
8693 \r
8694 // low-level front-end: append line to text edit / list widget\r
8695 void\r
8696 GLT_AddToList( char *name )\r
8697 {\r
8698     if( name != 0 ) {\r
8699             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8700     }\r
8701 }\r
8702 \r
8703 // low-level front-end: get line from text edit / list widget\r
8704 Boolean\r
8705 GLT_GetFromList( int index, char *name )\r
8706 {\r
8707     if( name != 0 ) {\r
8708             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8709                 return TRUE;\r
8710     }\r
8711     return FALSE;\r
8712 }\r
8713 \r
8714 void GLT_MoveSelection( HWND hDlg, int delta )\r
8715 {\r
8716     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8717     int idx2 = idx1 + delta;\r
8718     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8719 \r
8720     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8721         char buf[128];\r
8722 \r
8723         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8724         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8725         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8726         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8727     }\r
8728 }\r
8729 \r
8730 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8731 {\r
8732     switch( message )\r
8733     {\r
8734     case WM_INITDIALOG:\r
8735         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8736         \r
8737         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8738         Translate(hDlg, DLG_GameListOptions);\r
8739 \r
8740         /* Initialize list */\r
8741         GLT_TagsToList( lpUserGLT );\r
8742 \r
8743         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8744 \r
8745         break;\r
8746 \r
8747     case WM_COMMAND:\r
8748         switch( LOWORD(wParam) ) {\r
8749         case IDOK:\r
8750             GLT_ParseList();\r
8751             EndDialog( hDlg, 0 );\r
8752             return TRUE;\r
8753         case IDCANCEL:\r
8754             EndDialog( hDlg, 1 );\r
8755             return TRUE;\r
8756 \r
8757         case IDC_GLT_Default:\r
8758             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8759             return TRUE;\r
8760 \r
8761         case IDC_GLT_Restore:\r
8762             GLT_TagsToList( appData.gameListTags );\r
8763             return TRUE;\r
8764 \r
8765         case IDC_GLT_Up:\r
8766             GLT_MoveSelection( hDlg, -1 );\r
8767             return TRUE;\r
8768 \r
8769         case IDC_GLT_Down:\r
8770             GLT_MoveSelection( hDlg, +1 );\r
8771             return TRUE;\r
8772         }\r
8773 \r
8774         break;\r
8775     }\r
8776 \r
8777     return FALSE;\r
8778 }\r
8779 \r
8780 int GameListOptions()\r
8781 {\r
8782     int result;\r
8783     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8784 \r
8785       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8786 \r
8787     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8788 \r
8789     if( result == 0 ) {\r
8790         char *oldTags = appData.gameListTags;\r
8791         /* [AS] Memory leak here! */\r
8792         appData.gameListTags = strdup( lpUserGLT ); \r
8793         if(strcmp(oldTags, appData.gameListTags)) // [HGM] redo Game List when we changed something\r
8794             GameListToListBox(NULL, TRUE, ".", NULL, FALSE, FALSE); // "." as filter is kludge to select all\r
8795     }\r
8796 \r
8797     return result;\r
8798 }\r
8799 \r
8800 VOID\r
8801 DisplayIcsInteractionTitle(char *str)\r
8802 {\r
8803   char consoleTitle[MSG_SIZ];\r
8804 \r
8805     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8806     SetWindowText(hwndConsole, consoleTitle);\r
8807 \r
8808     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8809       char buf[MSG_SIZ], *p = buf, *q;\r
8810         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8811       do {\r
8812         q = strchr(p, ';');\r
8813         if(q) *q++ = 0;\r
8814         if(*p) ChatPopUp(p);\r
8815       } while(p=q);\r
8816     }\r
8817 \r
8818     SetActiveWindow(hwndMain);\r
8819 }\r
8820 \r
8821 void\r
8822 DrawPosition(int fullRedraw, Board board)\r
8823 {\r
8824   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8825 }\r
8826 \r
8827 void NotifyFrontendLogin()\r
8828 {\r
8829         if (hwndConsole)\r
8830                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8831 }\r
8832 \r
8833 VOID\r
8834 ResetFrontEnd()\r
8835 {\r
8836   fromX = fromY = -1;\r
8837   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8838     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8839     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8840     dragInfo.lastpos = dragInfo.pos;\r
8841     dragInfo.start.x = dragInfo.start.y = -1;\r
8842     dragInfo.from = dragInfo.start;\r
8843     ReleaseCapture();\r
8844     DrawPosition(TRUE, NULL);\r
8845   }\r
8846   TagsPopDown();\r
8847 }\r
8848 \r
8849 \r
8850 VOID\r
8851 CommentPopUp(char *title, char *str)\r
8852 {\r
8853   HWND hwnd = GetActiveWindow();\r
8854   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8855   SAY(str);\r
8856   SetActiveWindow(hwnd);\r
8857 }\r
8858 \r
8859 VOID\r
8860 CommentPopDown(void)\r
8861 {\r
8862   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8863   if (commentDialog) {\r
8864     ShowWindow(commentDialog, SW_HIDE);\r
8865   }\r
8866   commentUp = FALSE;\r
8867 }\r
8868 \r
8869 VOID\r
8870 EditCommentPopUp(int index, char *title, char *str)\r
8871 {\r
8872   EitherCommentPopUp(index, title, str, TRUE);\r
8873 }\r
8874 \r
8875 \r
8876 int\r
8877 Roar()\r
8878 {\r
8879   MyPlaySound(&sounds[(int)SoundRoar]);\r
8880   return 1;\r
8881 }\r
8882 \r
8883 VOID\r
8884 RingBell()\r
8885 {\r
8886   MyPlaySound(&sounds[(int)SoundMove]);\r
8887 }\r
8888 \r
8889 VOID PlayIcsWinSound()\r
8890 {\r
8891   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8892 }\r
8893 \r
8894 VOID PlayIcsLossSound()\r
8895 {\r
8896   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8897 }\r
8898 \r
8899 VOID PlayIcsDrawSound()\r
8900 {\r
8901   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8902 }\r
8903 \r
8904 VOID PlayIcsUnfinishedSound()\r
8905 {\r
8906   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8907 }\r
8908 \r
8909 VOID\r
8910 PlayAlarmSound()\r
8911 {\r
8912   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8913 }\r
8914 \r
8915 VOID\r
8916 PlayTellSound()\r
8917 {\r
8918   MyPlaySound(&textAttribs[ColorTell].sound);\r
8919 }\r
8920 \r
8921 \r
8922 VOID\r
8923 EchoOn()\r
8924 {\r
8925   HWND hInput;\r
8926   consoleEcho = TRUE;\r
8927   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8928   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8929   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8930 }\r
8931 \r
8932 \r
8933 VOID\r
8934 EchoOff()\r
8935 {\r
8936   CHARFORMAT cf;\r
8937   HWND hInput;\r
8938   consoleEcho = FALSE;\r
8939   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8940   /* This works OK: set text and background both to the same color */\r
8941   cf = consoleCF;\r
8942   cf.crTextColor = COLOR_ECHOOFF;\r
8943   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8944   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8945 }\r
8946 \r
8947 /* No Raw()...? */\r
8948 \r
8949 void Colorize(ColorClass cc, int continuation)\r
8950 {\r
8951   currentColorClass = cc;\r
8952   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8953   consoleCF.crTextColor = textAttribs[cc].color;\r
8954   consoleCF.dwEffects = textAttribs[cc].effects;\r
8955   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8956 }\r
8957 \r
8958 char *\r
8959 UserName()\r
8960 {\r
8961   static char buf[MSG_SIZ];\r
8962   DWORD bufsiz = MSG_SIZ;\r
8963 \r
8964   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8965         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8966   }\r
8967   if (!GetUserName(buf, &bufsiz)) {\r
8968     /*DisplayError("Error getting user name", GetLastError());*/\r
8969     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8970   }\r
8971   return buf;\r
8972 }\r
8973 \r
8974 char *\r
8975 HostName()\r
8976 {\r
8977   static char buf[MSG_SIZ];\r
8978   DWORD bufsiz = MSG_SIZ;\r
8979 \r
8980   if (!GetComputerName(buf, &bufsiz)) {\r
8981     /*DisplayError("Error getting host name", GetLastError());*/\r
8982     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8983   }\r
8984   return buf;\r
8985 }\r
8986 \r
8987 \r
8988 int\r
8989 ClockTimerRunning()\r
8990 {\r
8991   return clockTimerEvent != 0;\r
8992 }\r
8993 \r
8994 int\r
8995 StopClockTimer()\r
8996 {\r
8997   if (clockTimerEvent == 0) return FALSE;\r
8998   KillTimer(hwndMain, clockTimerEvent);\r
8999   clockTimerEvent = 0;\r
9000   return TRUE;\r
9001 }\r
9002 \r
9003 void\r
9004 StartClockTimer(long millisec)\r
9005 {\r
9006   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9007                              (UINT) millisec, NULL);\r
9008 }\r
9009 \r
9010 void\r
9011 DisplayWhiteClock(long timeRemaining, int highlight)\r
9012 {\r
9013   HDC hdc;\r
9014   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9015 \r
9016   if(appData.noGUI) return;\r
9017   hdc = GetDC(hwndMain);\r
9018   if (!IsIconic(hwndMain)) {\r
9019     DisplayAClock(hdc, timeRemaining, highlight, \r
9020                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
9021   }\r
9022   if (highlight && iconCurrent == iconBlack) {\r
9023     iconCurrent = iconWhite;\r
9024     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9025     if (IsIconic(hwndMain)) {\r
9026       DrawIcon(hdc, 2, 2, iconCurrent);\r
9027     }\r
9028   }\r
9029   (void) ReleaseDC(hwndMain, hdc);\r
9030   if (hwndConsole)\r
9031     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9032 }\r
9033 \r
9034 void\r
9035 DisplayBlackClock(long timeRemaining, int highlight)\r
9036 {\r
9037   HDC hdc;\r
9038   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9039 \r
9040 \r
9041   if(appData.noGUI) return;\r
9042   hdc = GetDC(hwndMain);\r
9043   if (!IsIconic(hwndMain)) {\r
9044     DisplayAClock(hdc, timeRemaining, highlight, \r
9045                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
9046   }\r
9047   if (highlight && iconCurrent == iconWhite) {\r
9048     iconCurrent = iconBlack;\r
9049     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9050     if (IsIconic(hwndMain)) {\r
9051       DrawIcon(hdc, 2, 2, iconCurrent);\r
9052     }\r
9053   }\r
9054   (void) ReleaseDC(hwndMain, hdc);\r
9055   if (hwndConsole)\r
9056     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9057 }\r
9058 \r
9059 \r
9060 int\r
9061 LoadGameTimerRunning()\r
9062 {\r
9063   return loadGameTimerEvent != 0;\r
9064 }\r
9065 \r
9066 int\r
9067 StopLoadGameTimer()\r
9068 {\r
9069   if (loadGameTimerEvent == 0) return FALSE;\r
9070   KillTimer(hwndMain, loadGameTimerEvent);\r
9071   loadGameTimerEvent = 0;\r
9072   return TRUE;\r
9073 }\r
9074 \r
9075 void\r
9076 StartLoadGameTimer(long millisec)\r
9077 {\r
9078   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9079                                 (UINT) millisec, NULL);\r
9080 }\r
9081 \r
9082 void\r
9083 AutoSaveGame()\r
9084 {\r
9085   char *defName;\r
9086   FILE *f;\r
9087   char fileTitle[MSG_SIZ];\r
9088 \r
9089   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9090   f = OpenFileDialog(hwndMain, "a", defName,\r
9091                      appData.oldSaveStyle ? "gam" : "pgn",\r
9092                      GAME_FILT, \r
9093                      _("Save Game to File"), NULL, fileTitle, NULL);\r
9094   if (f != NULL) {\r
9095     SaveGame(f, 0, "");\r
9096     fclose(f);\r
9097   }\r
9098 }\r
9099 \r
9100 \r
9101 void\r
9102 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9103 {\r
9104   if (delayedTimerEvent != 0) {\r
9105     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
9106       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9107     }\r
9108     KillTimer(hwndMain, delayedTimerEvent);\r
9109     delayedTimerEvent = 0;\r
9110     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
9111     delayedTimerCallback();\r
9112   }\r
9113   delayedTimerCallback = cb;\r
9114   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9115                                 (UINT) millisec, NULL);\r
9116 }\r
9117 \r
9118 DelayedEventCallback\r
9119 GetDelayedEvent()\r
9120 {\r
9121   if (delayedTimerEvent) {\r
9122     return delayedTimerCallback;\r
9123   } else {\r
9124     return NULL;\r
9125   }\r
9126 }\r
9127 \r
9128 void\r
9129 CancelDelayedEvent()\r
9130 {\r
9131   if (delayedTimerEvent) {\r
9132     KillTimer(hwndMain, delayedTimerEvent);\r
9133     delayedTimerEvent = 0;\r
9134   }\r
9135 }\r
9136 \r
9137 DWORD GetWin32Priority(int nice)\r
9138 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9139 /*\r
9140 REALTIME_PRIORITY_CLASS     0x00000100\r
9141 HIGH_PRIORITY_CLASS         0x00000080\r
9142 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9143 NORMAL_PRIORITY_CLASS       0x00000020\r
9144 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9145 IDLE_PRIORITY_CLASS         0x00000040\r
9146 */\r
9147         if (nice < -15) return 0x00000080;\r
9148         if (nice < 0)   return 0x00008000;\r
9149 \r
9150         if (nice == 0)  return 0x00000020;\r
9151         if (nice < 15)  return 0x00004000;\r
9152         return 0x00000040;\r
9153 }\r
9154 \r
9155 void RunCommand(char *cmdLine)\r
9156 {\r
9157   /* Now create the child process. */\r
9158   STARTUPINFO siStartInfo;\r
9159   PROCESS_INFORMATION piProcInfo;\r
9160 \r
9161   siStartInfo.cb = sizeof(STARTUPINFO);\r
9162   siStartInfo.lpReserved = NULL;\r
9163   siStartInfo.lpDesktop = NULL;\r
9164   siStartInfo.lpTitle = NULL;\r
9165   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9166   siStartInfo.cbReserved2 = 0;\r
9167   siStartInfo.lpReserved2 = NULL;\r
9168   siStartInfo.hStdInput = NULL;\r
9169   siStartInfo.hStdOutput = NULL;\r
9170   siStartInfo.hStdError = NULL;\r
9171 \r
9172   CreateProcess(NULL,\r
9173                 cmdLine,           /* command line */\r
9174                 NULL,      /* process security attributes */\r
9175                 NULL,      /* primary thread security attrs */\r
9176                 TRUE,      /* handles are inherited */\r
9177                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9178                 NULL,      /* use parent's environment */\r
9179                 NULL,\r
9180                 &siStartInfo, /* STARTUPINFO pointer */\r
9181                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
9182 \r
9183   CloseHandle(piProcInfo.hThread);\r
9184 }\r
9185 \r
9186 /* Start a child process running the given program.\r
9187    The process's standard output can be read from "from", and its\r
9188    standard input can be written to "to".\r
9189    Exit with fatal error if anything goes wrong.\r
9190    Returns an opaque pointer that can be used to destroy the process\r
9191    later.\r
9192 */\r
9193 int\r
9194 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9195 {\r
9196 #define BUFSIZE 4096\r
9197 \r
9198   HANDLE hChildStdinRd, hChildStdinWr,\r
9199     hChildStdoutRd, hChildStdoutWr;\r
9200   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9201   SECURITY_ATTRIBUTES saAttr;\r
9202   BOOL fSuccess;\r
9203   PROCESS_INFORMATION piProcInfo;\r
9204   STARTUPINFO siStartInfo;\r
9205   ChildProc *cp;\r
9206   char buf[MSG_SIZ];\r
9207   DWORD err;\r
9208 \r
9209   if (appData.debugMode) {\r
9210     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9211   }\r
9212 \r
9213   *pr = NoProc;\r
9214 \r
9215   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9216   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9217   saAttr.bInheritHandle = TRUE;\r
9218   saAttr.lpSecurityDescriptor = NULL;\r
9219 \r
9220   /*\r
9221    * The steps for redirecting child's STDOUT:\r
9222    *     1. Create anonymous pipe to be STDOUT for child.\r
9223    *     2. Create a noninheritable duplicate of read handle,\r
9224    *         and close the inheritable read handle.\r
9225    */\r
9226 \r
9227   /* Create a pipe for the child's STDOUT. */\r
9228   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9229     return GetLastError();\r
9230   }\r
9231 \r
9232   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9233   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9234                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9235                              FALSE,     /* not inherited */\r
9236                              DUPLICATE_SAME_ACCESS);\r
9237   if (! fSuccess) {\r
9238     return GetLastError();\r
9239   }\r
9240   CloseHandle(hChildStdoutRd);\r
9241 \r
9242   /*\r
9243    * The steps for redirecting child's STDIN:\r
9244    *     1. Create anonymous pipe to be STDIN for child.\r
9245    *     2. Create a noninheritable duplicate of write handle,\r
9246    *         and close the inheritable write handle.\r
9247    */\r
9248 \r
9249   /* Create a pipe for the child's STDIN. */\r
9250   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9251     return GetLastError();\r
9252   }\r
9253 \r
9254   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9255   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9256                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9257                              FALSE,     /* not inherited */\r
9258                              DUPLICATE_SAME_ACCESS);\r
9259   if (! fSuccess) {\r
9260     return GetLastError();\r
9261   }\r
9262   CloseHandle(hChildStdinWr);\r
9263 \r
9264   /* Arrange to (1) look in dir for the child .exe file, and\r
9265    * (2) have dir be the child's working directory.  Interpret\r
9266    * dir relative to the directory WinBoard loaded from. */\r
9267   GetCurrentDirectory(MSG_SIZ, buf);\r
9268   SetCurrentDirectory(installDir);\r
9269   SetCurrentDirectory(dir);\r
9270 \r
9271   /* Now create the child process. */\r
9272 \r
9273   siStartInfo.cb = sizeof(STARTUPINFO);\r
9274   siStartInfo.lpReserved = NULL;\r
9275   siStartInfo.lpDesktop = NULL;\r
9276   siStartInfo.lpTitle = NULL;\r
9277   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9278   siStartInfo.cbReserved2 = 0;\r
9279   siStartInfo.lpReserved2 = NULL;\r
9280   siStartInfo.hStdInput = hChildStdinRd;\r
9281   siStartInfo.hStdOutput = hChildStdoutWr;\r
9282   siStartInfo.hStdError = hChildStdoutWr;\r
9283 \r
9284   fSuccess = CreateProcess(NULL,\r
9285                            cmdLine,        /* command line */\r
9286                            NULL,           /* process security attributes */\r
9287                            NULL,           /* primary thread security attrs */\r
9288                            TRUE,           /* handles are inherited */\r
9289                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9290                            NULL,           /* use parent's environment */\r
9291                            NULL,\r
9292                            &siStartInfo, /* STARTUPINFO pointer */\r
9293                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9294 \r
9295   err = GetLastError();\r
9296   SetCurrentDirectory(buf); /* return to prev directory */\r
9297   if (! fSuccess) {\r
9298     return err;\r
9299   }\r
9300 \r
9301   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9302     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9303     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9304   }\r
9305 \r
9306   /* Close the handles we don't need in the parent */\r
9307   CloseHandle(piProcInfo.hThread);\r
9308   CloseHandle(hChildStdinRd);\r
9309   CloseHandle(hChildStdoutWr);\r
9310 \r
9311   /* Prepare return value */\r
9312   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9313   cp->kind = CPReal;\r
9314   cp->hProcess = piProcInfo.hProcess;\r
9315   cp->pid = piProcInfo.dwProcessId;\r
9316   cp->hFrom = hChildStdoutRdDup;\r
9317   cp->hTo = hChildStdinWrDup;\r
9318 \r
9319   *pr = (void *) cp;\r
9320 \r
9321   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9322      2000 where engines sometimes don't see the initial command(s)\r
9323      from WinBoard and hang.  I don't understand how that can happen,\r
9324      but the Sleep is harmless, so I've put it in.  Others have also\r
9325      reported what may be the same problem, so hopefully this will fix\r
9326      it for them too.  */\r
9327   Sleep(500);\r
9328 \r
9329   return NO_ERROR;\r
9330 }\r
9331 \r
9332 \r
9333 void\r
9334 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9335 {\r
9336   ChildProc *cp; int result;\r
9337 \r
9338   cp = (ChildProc *) pr;\r
9339   if (cp == NULL) return;\r
9340 \r
9341   switch (cp->kind) {\r
9342   case CPReal:\r
9343     /* TerminateProcess is considered harmful, so... */\r
9344     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9345     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9346     /* The following doesn't work because the chess program\r
9347        doesn't "have the same console" as WinBoard.  Maybe\r
9348        we could arrange for this even though neither WinBoard\r
9349        nor the chess program uses a console for stdio? */\r
9350     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9351 \r
9352     /* [AS] Special termination modes for misbehaving programs... */\r
9353     if( signal & 8 ) { \r
9354         result = TerminateProcess( cp->hProcess, 0 );\r
9355 \r
9356         if ( appData.debugMode) {\r
9357             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9358         }\r
9359     }\r
9360     else if( signal & 4 ) {\r
9361         DWORD dw = WaitForSingleObject( cp->hProcess, appData.delayAfterQuit*1000 + 50 ); // Wait 3 seconds at most\r
9362 \r
9363         if( dw != WAIT_OBJECT_0 ) {\r
9364             result = TerminateProcess( cp->hProcess, 0 );\r
9365 \r
9366             if ( appData.debugMode) {\r
9367                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9368             }\r
9369 \r
9370         }\r
9371     }\r
9372 \r
9373     CloseHandle(cp->hProcess);\r
9374     break;\r
9375 \r
9376   case CPComm:\r
9377     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9378     break;\r
9379 \r
9380   case CPSock:\r
9381     closesocket(cp->sock);\r
9382     WSACleanup();\r
9383     break;\r
9384 \r
9385   case CPRcmd:\r
9386     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9387     closesocket(cp->sock);\r
9388     closesocket(cp->sock2);\r
9389     WSACleanup();\r
9390     break;\r
9391   }\r
9392   free(cp);\r
9393 }\r
9394 \r
9395 void\r
9396 InterruptChildProcess(ProcRef pr)\r
9397 {\r
9398   ChildProc *cp;\r
9399 \r
9400   cp = (ChildProc *) pr;\r
9401   if (cp == NULL) return;\r
9402   switch (cp->kind) {\r
9403   case CPReal:\r
9404     /* The following doesn't work because the chess program\r
9405        doesn't "have the same console" as WinBoard.  Maybe\r
9406        we could arrange for this even though neither WinBoard\r
9407        nor the chess program uses a console for stdio */\r
9408     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9409     break;\r
9410 \r
9411   case CPComm:\r
9412   case CPSock:\r
9413     /* Can't interrupt */\r
9414     break;\r
9415 \r
9416   case CPRcmd:\r
9417     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9418     break;\r
9419   }\r
9420 }\r
9421 \r
9422 \r
9423 int\r
9424 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9425 {\r
9426   char cmdLine[MSG_SIZ];\r
9427 \r
9428   if (port[0] == NULLCHAR) {\r
9429     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9430   } else {\r
9431     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9432   }\r
9433   return StartChildProcess(cmdLine, "", pr);\r
9434 }\r
9435 \r
9436 \r
9437 /* Code to open TCP sockets */\r
9438 \r
9439 int\r
9440 OpenTCP(char *host, char *port, ProcRef *pr)\r
9441 {\r
9442   ChildProc *cp;\r
9443   int err;\r
9444   SOCKET s;\r
9445 \r
9446   struct sockaddr_in sa, mysa;\r
9447   struct hostent FAR *hp;\r
9448   unsigned short uport;\r
9449   WORD wVersionRequested;\r
9450   WSADATA wsaData;\r
9451 \r
9452   /* Initialize socket DLL */\r
9453   wVersionRequested = MAKEWORD(1, 1);\r
9454   err = WSAStartup(wVersionRequested, &wsaData);\r
9455   if (err != 0) return err;\r
9456 \r
9457   /* Make socket */\r
9458   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9459     err = WSAGetLastError();\r
9460     WSACleanup();\r
9461     return err;\r
9462   }\r
9463 \r
9464   /* Bind local address using (mostly) don't-care values.\r
9465    */\r
9466   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9467   mysa.sin_family = AF_INET;\r
9468   mysa.sin_addr.s_addr = INADDR_ANY;\r
9469   uport = (unsigned short) 0;\r
9470   mysa.sin_port = htons(uport);\r
9471   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9472       == SOCKET_ERROR) {\r
9473     err = WSAGetLastError();\r
9474     WSACleanup();\r
9475     return err;\r
9476   }\r
9477 \r
9478   /* Resolve remote host name */\r
9479   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9480   if (!(hp = gethostbyname(host))) {\r
9481     unsigned int b0, b1, b2, b3;\r
9482 \r
9483     err = WSAGetLastError();\r
9484 \r
9485     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9486       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9487       hp->h_addrtype = AF_INET;\r
9488       hp->h_length = 4;\r
9489       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9490       hp->h_addr_list[0] = (char *) malloc(4);\r
9491       hp->h_addr_list[0][0] = (char) b0;\r
9492       hp->h_addr_list[0][1] = (char) b1;\r
9493       hp->h_addr_list[0][2] = (char) b2;\r
9494       hp->h_addr_list[0][3] = (char) b3;\r
9495     } else {\r
9496       WSACleanup();\r
9497       return err;\r
9498     }\r
9499   }\r
9500   sa.sin_family = hp->h_addrtype;\r
9501   uport = (unsigned short) atoi(port);\r
9502   sa.sin_port = htons(uport);\r
9503   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9504 \r
9505   /* Make connection */\r
9506   if (connect(s, (struct sockaddr *) &sa,\r
9507               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9508     err = WSAGetLastError();\r
9509     WSACleanup();\r
9510     return err;\r
9511   }\r
9512 \r
9513   /* Prepare return value */\r
9514   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9515   cp->kind = CPSock;\r
9516   cp->sock = s;\r
9517   *pr = (ProcRef *) cp;\r
9518 \r
9519   return NO_ERROR;\r
9520 }\r
9521 \r
9522 int\r
9523 OpenCommPort(char *name, ProcRef *pr)\r
9524 {\r
9525   HANDLE h;\r
9526   COMMTIMEOUTS ct;\r
9527   ChildProc *cp;\r
9528   char fullname[MSG_SIZ];\r
9529 \r
9530   if (*name != '\\')\r
9531     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9532   else\r
9533     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9534 \r
9535   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9536                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9537   if (h == (HANDLE) -1) {\r
9538     return GetLastError();\r
9539   }\r
9540   hCommPort = h;\r
9541 \r
9542   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9543 \r
9544   /* Accumulate characters until a 100ms pause, then parse */\r
9545   ct.ReadIntervalTimeout = 100;\r
9546   ct.ReadTotalTimeoutMultiplier = 0;\r
9547   ct.ReadTotalTimeoutConstant = 0;\r
9548   ct.WriteTotalTimeoutMultiplier = 0;\r
9549   ct.WriteTotalTimeoutConstant = 0;\r
9550   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9551 \r
9552   /* Prepare return value */\r
9553   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9554   cp->kind = CPComm;\r
9555   cp->hFrom = h;\r
9556   cp->hTo = h;\r
9557   *pr = (ProcRef *) cp;\r
9558 \r
9559   return NO_ERROR;\r
9560 }\r
9561 \r
9562 int\r
9563 OpenLoopback(ProcRef *pr)\r
9564 {\r
9565   DisplayFatalError(_("Not implemented"), 0, 1);\r
9566   return NO_ERROR;\r
9567 }\r
9568 \r
9569 \r
9570 int\r
9571 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9572 {\r
9573   ChildProc *cp;\r
9574   int err;\r
9575   SOCKET s, s2, s3;\r
9576   struct sockaddr_in sa, mysa;\r
9577   struct hostent FAR *hp;\r
9578   unsigned short uport;\r
9579   WORD wVersionRequested;\r
9580   WSADATA wsaData;\r
9581   int fromPort;\r
9582   char stderrPortStr[MSG_SIZ];\r
9583 \r
9584   /* Initialize socket DLL */\r
9585   wVersionRequested = MAKEWORD(1, 1);\r
9586   err = WSAStartup(wVersionRequested, &wsaData);\r
9587   if (err != 0) return err;\r
9588 \r
9589   /* Resolve remote host name */\r
9590   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9591   if (!(hp = gethostbyname(host))) {\r
9592     unsigned int b0, b1, b2, b3;\r
9593 \r
9594     err = WSAGetLastError();\r
9595 \r
9596     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9597       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9598       hp->h_addrtype = AF_INET;\r
9599       hp->h_length = 4;\r
9600       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9601       hp->h_addr_list[0] = (char *) malloc(4);\r
9602       hp->h_addr_list[0][0] = (char) b0;\r
9603       hp->h_addr_list[0][1] = (char) b1;\r
9604       hp->h_addr_list[0][2] = (char) b2;\r
9605       hp->h_addr_list[0][3] = (char) b3;\r
9606     } else {\r
9607       WSACleanup();\r
9608       return err;\r
9609     }\r
9610   }\r
9611   sa.sin_family = hp->h_addrtype;\r
9612   uport = (unsigned short) 514;\r
9613   sa.sin_port = htons(uport);\r
9614   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9615 \r
9616   /* Bind local socket to unused "privileged" port address\r
9617    */\r
9618   s = INVALID_SOCKET;\r
9619   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9620   mysa.sin_family = AF_INET;\r
9621   mysa.sin_addr.s_addr = INADDR_ANY;\r
9622   for (fromPort = 1023;; fromPort--) {\r
9623     if (fromPort < 0) {\r
9624       WSACleanup();\r
9625       return WSAEADDRINUSE;\r
9626     }\r
9627     if (s == INVALID_SOCKET) {\r
9628       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9629         err = WSAGetLastError();\r
9630         WSACleanup();\r
9631         return err;\r
9632       }\r
9633     }\r
9634     uport = (unsigned short) fromPort;\r
9635     mysa.sin_port = htons(uport);\r
9636     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9637         == SOCKET_ERROR) {\r
9638       err = WSAGetLastError();\r
9639       if (err == WSAEADDRINUSE) continue;\r
9640       WSACleanup();\r
9641       return err;\r
9642     }\r
9643     if (connect(s, (struct sockaddr *) &sa,\r
9644       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9645       err = WSAGetLastError();\r
9646       if (err == WSAEADDRINUSE) {\r
9647         closesocket(s);\r
9648         s = -1;\r
9649         continue;\r
9650       }\r
9651       WSACleanup();\r
9652       return err;\r
9653     }\r
9654     break;\r
9655   }\r
9656 \r
9657   /* Bind stderr local socket to unused "privileged" port address\r
9658    */\r
9659   s2 = INVALID_SOCKET;\r
9660   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9661   mysa.sin_family = AF_INET;\r
9662   mysa.sin_addr.s_addr = INADDR_ANY;\r
9663   for (fromPort = 1023;; fromPort--) {\r
9664     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9665     if (fromPort < 0) {\r
9666       (void) closesocket(s);\r
9667       WSACleanup();\r
9668       return WSAEADDRINUSE;\r
9669     }\r
9670     if (s2 == INVALID_SOCKET) {\r
9671       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9672         err = WSAGetLastError();\r
9673         closesocket(s);\r
9674         WSACleanup();\r
9675         return err;\r
9676       }\r
9677     }\r
9678     uport = (unsigned short) fromPort;\r
9679     mysa.sin_port = htons(uport);\r
9680     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9681         == SOCKET_ERROR) {\r
9682       err = WSAGetLastError();\r
9683       if (err == WSAEADDRINUSE) continue;\r
9684       (void) closesocket(s);\r
9685       WSACleanup();\r
9686       return err;\r
9687     }\r
9688     if (listen(s2, 1) == SOCKET_ERROR) {\r
9689       err = WSAGetLastError();\r
9690       if (err == WSAEADDRINUSE) {\r
9691         closesocket(s2);\r
9692         s2 = INVALID_SOCKET;\r
9693         continue;\r
9694       }\r
9695       (void) closesocket(s);\r
9696       (void) closesocket(s2);\r
9697       WSACleanup();\r
9698       return err;\r
9699     }\r
9700     break;\r
9701   }\r
9702   prevStderrPort = fromPort; // remember port used\r
9703   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9704 \r
9705   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9706     err = WSAGetLastError();\r
9707     (void) closesocket(s);\r
9708     (void) closesocket(s2);\r
9709     WSACleanup();\r
9710     return err;\r
9711   }\r
9712 \r
9713   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9714     err = WSAGetLastError();\r
9715     (void) closesocket(s);\r
9716     (void) closesocket(s2);\r
9717     WSACleanup();\r
9718     return err;\r
9719   }\r
9720   if (*user == NULLCHAR) user = UserName();\r
9721   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9722     err = WSAGetLastError();\r
9723     (void) closesocket(s);\r
9724     (void) closesocket(s2);\r
9725     WSACleanup();\r
9726     return err;\r
9727   }\r
9728   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9729     err = WSAGetLastError();\r
9730     (void) closesocket(s);\r
9731     (void) closesocket(s2);\r
9732     WSACleanup();\r
9733     return err;\r
9734   }\r
9735 \r
9736   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9737     err = WSAGetLastError();\r
9738     (void) closesocket(s);\r
9739     (void) closesocket(s2);\r
9740     WSACleanup();\r
9741     return err;\r
9742   }\r
9743   (void) closesocket(s2);  /* Stop listening */\r
9744 \r
9745   /* Prepare return value */\r
9746   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9747   cp->kind = CPRcmd;\r
9748   cp->sock = s;\r
9749   cp->sock2 = s3;\r
9750   *pr = (ProcRef *) cp;\r
9751 \r
9752   return NO_ERROR;\r
9753 }\r
9754 \r
9755 \r
9756 InputSourceRef\r
9757 AddInputSource(ProcRef pr, int lineByLine,\r
9758                InputCallback func, VOIDSTAR closure)\r
9759 {\r
9760   InputSource *is, *is2 = NULL;\r
9761   ChildProc *cp = (ChildProc *) pr;\r
9762 \r
9763   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9764   is->lineByLine = lineByLine;\r
9765   is->func = func;\r
9766   is->closure = closure;\r
9767   is->second = NULL;\r
9768   is->next = is->buf;\r
9769   if (pr == NoProc) {\r
9770     is->kind = CPReal;\r
9771     consoleInputSource = is;\r
9772   } else {\r
9773     is->kind = cp->kind;\r
9774     /* \r
9775         [AS] Try to avoid a race condition if the thread is given control too early:\r
9776         we create all threads suspended so that the is->hThread variable can be\r
9777         safely assigned, then let the threads start with ResumeThread.\r
9778     */\r
9779     switch (cp->kind) {\r
9780     case CPReal:\r
9781       is->hFile = cp->hFrom;\r
9782       cp->hFrom = NULL; /* now owned by InputThread */\r
9783       is->hThread =\r
9784         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9785                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9786       break;\r
9787 \r
9788     case CPComm:\r
9789       is->hFile = cp->hFrom;\r
9790       cp->hFrom = NULL; /* now owned by InputThread */\r
9791       is->hThread =\r
9792         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9793                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9794       break;\r
9795 \r
9796     case CPSock:\r
9797       is->sock = cp->sock;\r
9798       is->hThread =\r
9799         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9800                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9801       break;\r
9802 \r
9803     case CPRcmd:\r
9804       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9805       *is2 = *is;\r
9806       is->sock = cp->sock;\r
9807       is->second = is2;\r
9808       is2->sock = cp->sock2;\r
9809       is2->second = is2;\r
9810       is->hThread =\r
9811         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9812                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9813       is2->hThread =\r
9814         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9815                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9816       break;\r
9817     }\r
9818 \r
9819     if( is->hThread != NULL ) {\r
9820         ResumeThread( is->hThread );\r
9821     }\r
9822 \r
9823     if( is2 != NULL && is2->hThread != NULL ) {\r
9824         ResumeThread( is2->hThread );\r
9825     }\r
9826   }\r
9827 \r
9828   return (InputSourceRef) is;\r
9829 }\r
9830 \r
9831 void\r
9832 RemoveInputSource(InputSourceRef isr)\r
9833 {\r
9834   InputSource *is;\r
9835 \r
9836   is = (InputSource *) isr;\r
9837   is->hThread = NULL;  /* tell thread to stop */\r
9838   CloseHandle(is->hThread);\r
9839   if (is->second != NULL) {\r
9840     is->second->hThread = NULL;\r
9841     CloseHandle(is->second->hThread);\r
9842   }\r
9843 }\r
9844 \r
9845 int no_wrap(char *message, int count)\r
9846 {\r
9847     ConsoleOutput(message, count, FALSE);\r
9848     return count;\r
9849 }\r
9850 \r
9851 int\r
9852 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9853 {\r
9854   DWORD dOutCount;\r
9855   int outCount = SOCKET_ERROR;\r
9856   ChildProc *cp = (ChildProc *) pr;\r
9857   static OVERLAPPED ovl;\r
9858 \r
9859   static int line = 0;\r
9860 \r
9861   if (pr == NoProc)\r
9862   {\r
9863     if (appData.noJoin || !appData.useInternalWrap)\r
9864       return no_wrap(message, count);\r
9865     else\r
9866     {\r
9867       int width = get_term_width();\r
9868       int len = wrap(NULL, message, count, width, &line);\r
9869       char *msg = malloc(len);\r
9870       int dbgchk;\r
9871 \r
9872       if (!msg)\r
9873         return no_wrap(message, count);\r
9874       else\r
9875       {\r
9876         dbgchk = wrap(msg, message, count, width, &line);\r
9877         if (dbgchk != len && appData.debugMode)\r
9878             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9879         ConsoleOutput(msg, len, FALSE);\r
9880         free(msg);\r
9881         return len;\r
9882       }\r
9883     }\r
9884   }\r
9885 \r
9886   if (ovl.hEvent == NULL) {\r
9887     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9888   }\r
9889   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9890 \r
9891   switch (cp->kind) {\r
9892   case CPSock:\r
9893   case CPRcmd:\r
9894     outCount = send(cp->sock, message, count, 0);\r
9895     if (outCount == SOCKET_ERROR) {\r
9896       *outError = WSAGetLastError();\r
9897     } else {\r
9898       *outError = NO_ERROR;\r
9899     }\r
9900     break;\r
9901 \r
9902   case CPReal:\r
9903     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9904                   &dOutCount, NULL)) {\r
9905       *outError = NO_ERROR;\r
9906       outCount = (int) dOutCount;\r
9907     } else {\r
9908       *outError = GetLastError();\r
9909     }\r
9910     break;\r
9911 \r
9912   case CPComm:\r
9913     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9914                             &dOutCount, &ovl);\r
9915     if (*outError == NO_ERROR) {\r
9916       outCount = (int) dOutCount;\r
9917     }\r
9918     break;\r
9919   }\r
9920   return outCount;\r
9921 }\r
9922 \r
9923 void\r
9924 DoSleep(int n)\r
9925 {\r
9926     if(n != 0) Sleep(n);\r
9927 }\r
9928 \r
9929 int\r
9930 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9931                        long msdelay)\r
9932 {\r
9933   /* Ignore delay, not implemented for WinBoard */\r
9934   return OutputToProcess(pr, message, count, outError);\r
9935 }\r
9936 \r
9937 \r
9938 void\r
9939 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9940                         char *buf, int count, int error)\r
9941 {\r
9942   DisplayFatalError(_("Not implemented"), 0, 1);\r
9943 }\r
9944 \r
9945 /* see wgamelist.c for Game List functions */\r
9946 /* see wedittags.c for Edit Tags functions */\r
9947 \r
9948 \r
9949 int\r
9950 ICSInitScript()\r
9951 {\r
9952   FILE *f;\r
9953   char buf[MSG_SIZ];\r
9954   char *dummy;\r
9955 \r
9956   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9957     f = fopen(buf, "r");\r
9958     if (f != NULL) {\r
9959       ProcessICSInitScript(f);\r
9960       fclose(f);\r
9961       return TRUE;\r
9962     }\r
9963   }\r
9964   return FALSE;\r
9965 }\r
9966 \r
9967 \r
9968 VOID\r
9969 StartAnalysisClock()\r
9970 {\r
9971   if (analysisTimerEvent) return;\r
9972   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9973                                         (UINT) 2000, NULL);\r
9974 }\r
9975 \r
9976 VOID\r
9977 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9978 {\r
9979   highlightInfo.sq[0].x = fromX;\r
9980   highlightInfo.sq[0].y = fromY;\r
9981   highlightInfo.sq[1].x = toX;\r
9982   highlightInfo.sq[1].y = toY;\r
9983 }\r
9984 \r
9985 VOID\r
9986 ClearHighlights()\r
9987 {\r
9988   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9989     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9990 }\r
9991 \r
9992 VOID\r
9993 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9994 {\r
9995   premoveHighlightInfo.sq[0].x = fromX;\r
9996   premoveHighlightInfo.sq[0].y = fromY;\r
9997   premoveHighlightInfo.sq[1].x = toX;\r
9998   premoveHighlightInfo.sq[1].y = toY;\r
9999 }\r
10000 \r
10001 VOID\r
10002 ClearPremoveHighlights()\r
10003 {\r
10004   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10005     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10006 }\r
10007 \r
10008 VOID\r
10009 ShutDownFrontEnd()\r
10010 {\r
10011   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10012   DeleteClipboardTempFiles();\r
10013 }\r
10014 \r
10015 void\r
10016 BoardToTop()\r
10017 {\r
10018     if (IsIconic(hwndMain))\r
10019       ShowWindow(hwndMain, SW_RESTORE);\r
10020 \r
10021     SetActiveWindow(hwndMain);\r
10022 }\r
10023 \r
10024 /*\r
10025  * Prototypes for animation support routines\r
10026  */\r
10027 static void ScreenSquare(int column, int row, POINT * pt);\r
10028 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10029      POINT frames[], int * nFrames);\r
10030 \r
10031 \r
10032 #define kFactor 4\r
10033 \r
10034 void\r
10035 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
10036 {       // [HGM] atomic: animate blast wave\r
10037         int i;\r
10038 \r
10039         explodeInfo.fromX = fromX;\r
10040         explodeInfo.fromY = fromY;\r
10041         explodeInfo.toX = toX;\r
10042         explodeInfo.toY = toY;\r
10043         for(i=1; i<4*kFactor; i++) {\r
10044             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
10045             DrawPosition(FALSE, board);\r
10046             Sleep(appData.animSpeed);\r
10047         }\r
10048         explodeInfo.radius = 0;\r
10049         DrawPosition(TRUE, board);\r
10050 }\r
10051 \r
10052 void\r
10053 AnimateMove(board, fromX, fromY, toX, toY)\r
10054      Board board;\r
10055      int fromX;\r
10056      int fromY;\r
10057      int toX;\r
10058      int toY;\r
10059 {\r
10060   ChessSquare piece, victim = EmptySquare, victim2 = EmptySquare;\r
10061   int x = toX, y = toY, x2 = kill2X;\r
10062   POINT start, finish, mid;\r
10063   POINT frames[kFactor * 2 + 1];\r
10064   int nFrames, n;\r
10065 \r
10066   if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();\r
10067 \r
10068   if (!appData.animate) return;\r
10069   if (doingSizing) return;\r
10070   if (fromY < 0 || fromX < 0) return;\r
10071   piece = board[fromY][fromX];\r
10072   if (piece >= EmptySquare) return;\r
10073 \r
10074   if(x2 >= 0) toX = kill2X, toY = kill2Y,  victim = board[killY][killX], victim2 = board[kill2Y][kill2X]; else\r
10075   if(killX >= 0) toX = killX, toY = killY, victim = board[killY][killX]; // [HGM] lion: first to kill square\r
10076 \r
10077   animInfo.from.x = fromX;\r
10078   animInfo.from.y = fromY;\r
10079 \r
10080 again:\r
10081 \r
10082   ScreenSquare(fromX, fromY, &start);\r
10083   ScreenSquare(toX, toY, &finish);\r
10084 \r
10085   /* All moves except knight jumps move in straight line */\r
10086   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
10087     mid.x = start.x + (finish.x - start.x) / 2;\r
10088     mid.y = start.y + (finish.y - start.y) / 2;\r
10089   } else {\r
10090     /* Knight: make straight movement then diagonal */\r
10091     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10092        mid.x = start.x + (finish.x - start.x) / 2;\r
10093        mid.y = start.y;\r
10094      } else {\r
10095        mid.x = start.x;\r
10096        mid.y = start.y + (finish.y - start.y) / 2;\r
10097      }\r
10098   }\r
10099   \r
10100   /* Don't use as many frames for very short moves */\r
10101   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10102     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10103   else\r
10104     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10105 \r
10106   animInfo.to.x = toX;\r
10107   animInfo.to.y = toY;\r
10108   animInfo.lastpos = start;\r
10109   animInfo.piece = piece;\r
10110   for (n = 0; n < nFrames; n++) {\r
10111     animInfo.pos = frames[n];\r
10112     DrawPosition(FALSE, board);\r
10113     animInfo.lastpos = animInfo.pos;\r
10114     Sleep(appData.animSpeed);\r
10115   }\r
10116   animInfo.pos = finish;\r
10117   DrawPosition(FALSE, board);\r
10118 \r
10119   if(toX == x2 && toY == kill2Y) {\r
10120     fromX = toX; fromY = toY; toX = killX; toY = killY; x2 = -1;\r
10121     board[kill2Y][kill2X] = EmptySquare; goto again;\r
10122   } // second leg\r
10123   if(toX != x || toY != y) {\r
10124     fromX = toX; fromY = toY; toX = x; toY = y;\r
10125     board[killY][killX] = EmptySquare; goto again;\r
10126   } // second leg\r
10127 \r
10128 if(victim2 != EmptySquare) board[kill2Y][kill2X] = victim2;\r
10129 if(victim  != EmptySquare) board[killY][killX] = victim;\r
10130 \r
10131   animInfo.piece = EmptySquare;\r
10132   Explode(board, fromX, fromY, toX, toY);\r
10133 }\r
10134 \r
10135 /*      Convert board position to corner of screen rect and color       */\r
10136 \r
10137 static void\r
10138 ScreenSquare(column, row, pt)\r
10139      int column; int row; POINT * pt;\r
10140 {\r
10141   if (flipView) {\r
10142     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
10143     pt->y = lineGap + row * (squareSize + lineGap) + border;\r
10144   } else {\r
10145     pt->x = lineGap + column * (squareSize + lineGap) + border;\r
10146     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
10147   }\r
10148 }\r
10149 \r
10150 /*      Generate a series of frame coords from start->mid->finish.\r
10151         The movement rate doubles until the half way point is\r
10152         reached, then halves back down to the final destination,\r
10153         which gives a nice slow in/out effect. The algorithmn\r
10154         may seem to generate too many intermediates for short\r
10155         moves, but remember that the purpose is to attract the\r
10156         viewers attention to the piece about to be moved and\r
10157         then to where it ends up. Too few frames would be less\r
10158         noticeable.                                             */\r
10159 \r
10160 static void\r
10161 Tween(start, mid, finish, factor, frames, nFrames)\r
10162      POINT * start; POINT * mid;\r
10163      POINT * finish; int factor;\r
10164      POINT frames[]; int * nFrames;\r
10165 {\r
10166   int n, fraction = 1, count = 0;\r
10167 \r
10168   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10169   for (n = 0; n < factor; n++)\r
10170     fraction *= 2;\r
10171   for (n = 0; n < factor; n++) {\r
10172     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10173     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10174     count ++;\r
10175     fraction = fraction / 2;\r
10176   }\r
10177   \r
10178   /* Midpoint */\r
10179   frames[count] = *mid;\r
10180   count ++;\r
10181   \r
10182   /* Slow out, stepping 1/2, then 1/4, ... */\r
10183   fraction = 2;\r
10184   for (n = 0; n < factor; n++) {\r
10185     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10186     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10187     count ++;\r
10188     fraction = fraction * 2;\r
10189   }\r
10190   *nFrames = count;\r
10191 }\r
10192 \r
10193 void\r
10194 SettingsPopUp(ChessProgramState *cps)\r
10195 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
10196       EngineOptionsPopup(savedHwnd, cps);\r
10197 }\r
10198 \r
10199 int flock(int fid, int code)\r
10200 {\r
10201     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
10202     OVERLAPPED ov;\r
10203     ov.hEvent = NULL;\r
10204     ov.Offset = 0;\r
10205     ov.OffsetHigh = 0;\r
10206     switch(code) {\r
10207       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
10208 \r
10209       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
10210       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
10211       default: return -1;\r
10212     }\r
10213     return 0;\r
10214 }\r
10215 \r
10216 char *\r
10217 Col2Text (int n)\r
10218 {\r
10219     static int i=0;\r
10220     static char col[8][20];\r
10221     COLORREF color = *(COLORREF *) colorVariable[n];\r
10222     i = i+1 & 7;\r
10223     snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
10224     return col[i];\r
10225 }\r
10226 \r
10227 void\r
10228 ActivateTheme (int new)\r
10229 {   // Redo initialization of features depending on options that can occur in themes\r
10230    InitTextures();\r
10231    if(new) InitDrawingColors();\r
10232    fontBitmapSquareSize = 0; // request creation of new font pieces\r
10233    InitDrawingSizes(boardSize, 0);\r
10234    InvalidateRect(hwndMain, NULL, TRUE);\r
10235 }\r