5b57ba3fd19bc5285d642e7f438a02db1d4b33b4
[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 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 #include <io.h>\r
77 \r
78 #if __GNUC__\r
79 #include <errno.h>\r
80 #include <string.h>\r
81 #endif\r
82 \r
83 #include "common.h"\r
84 #include "frontend.h"\r
85 #include "backend.h"\r
86 #include "winboard.h"\r
87 #include "moves.h"\r
88 #include "wclipbrd.h"\r
89 #include "woptions.h"\r
90 #include "wsockerr.h"\r
91 #include "defaults.h"\r
92 #include "help.h"\r
93 #include "wsnap.h"\r
94 \r
95 #define SLASH '/'\r
96 #define DATADIR "~~"\r
97 \r
98 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
99 \r
100   int myrandom(void);\r
101   void mysrandom(unsigned int seed);\r
102 \r
103 extern int whiteFlag, blackFlag;\r
104 Boolean flipClock = FALSE;\r
105 extern HANDLE chatHandle[];\r
106 extern enum ICS_TYPE ics_type;\r
107 \r
108 int  MySearchPath P((char *installDir, char *name, char *fullname));\r
109 int  MyGetFullPathName P((char *name, char *fullname));\r
110 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
111 VOID NewVariantPopup(HWND hwnd);\r
112 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
113                    /*char*/int promoChar));\r
114 void DisplayMove P((int moveNumber));\r
115 void ChatPopUp P((char *s));\r
116 typedef struct {\r
117   ChessSquare piece;  \r
118   POINT pos;      /* window coordinates of current pos */\r
119   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
120   POINT from;     /* board coordinates of the piece's orig pos */\r
121   POINT to;       /* board coordinates of the piece's new pos */\r
122 } AnimInfo;\r
123 \r
124 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
125 \r
126 typedef struct {\r
127   POINT start;    /* window coordinates of start pos */\r
128   POINT pos;      /* window coordinates of current pos */\r
129   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
130   POINT from;     /* board coordinates of the piece's orig pos */\r
131   ChessSquare piece;\r
132 } DragInfo;\r
133 \r
134 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
135 \r
136 typedef struct {\r
137   POINT sq[2];    /* board coordinates of from, to squares */\r
138 } HighlightInfo;\r
139 \r
140 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
141 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
142 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
143 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
144 \r
145 typedef struct { // [HGM] atomic\r
146   int fromX, fromY, toX, toY, radius;\r
147 } ExplodeInfo;\r
148 \r
149 static ExplodeInfo explodeInfo;\r
150 \r
151 /* Window class names */\r
152 char szAppName[] = "WinBoard";\r
153 char szConsoleName[] = "WBConsole";\r
154 \r
155 /* Title bar text */\r
156 char szTitle[] = "WinBoard";\r
157 char szConsoleTitle[] = "I C S Interaction";\r
158 \r
159 char *programName;\r
160 char *settingsFileName;\r
161 Boolean saveSettingsOnExit;\r
162 char installDir[MSG_SIZ];\r
163 int errorExitStatus;\r
164 \r
165 BoardSize boardSize;\r
166 Boolean chessProgram;\r
167 //static int boardX, boardY;\r
168 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
169 int squareSize, lineGap, minorSize;\r
170 static int winW, winH;\r
171 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
172 static int logoHeight = 0;\r
173 static char messageText[MESSAGE_TEXT_MAX];\r
174 static int clockTimerEvent = 0;\r
175 static int loadGameTimerEvent = 0;\r
176 static int analysisTimerEvent = 0;\r
177 static DelayedEventCallback delayedTimerCallback;\r
178 static int delayedTimerEvent = 0;\r
179 static int buttonCount = 2;\r
180 char *icsTextMenuString;\r
181 char *icsNames;\r
182 char *firstChessProgramNames;\r
183 char *secondChessProgramNames;\r
184 \r
185 #define PALETTESIZE 256\r
186 \r
187 HINSTANCE hInst;          /* current instance */\r
188 Boolean alwaysOnTop = FALSE;\r
189 RECT boardRect;\r
190 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
191   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
192 COLORREF markerColor[8] = { 0x00FFFF, 0x0000FF, 0x00FF00, 0xFF0000, 0xFFFF00, 0xFF00FF, 0xFFFFFF, 0x000000 };\r
193 HPALETTE hPal;\r
194 ColorClass currentColorClass;\r
195 \r
196 static HWND savedHwnd;\r
197 HWND hCommPort = NULL;    /* currently open comm port */\r
198 static HWND hwndPause;    /* pause button */\r
199 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
200 static HBRUSH lightSquareBrush, darkSquareBrush,\r
201   blackSquareBrush, /* [HGM] for band between board and holdings */\r
202   explodeBrush,     /* [HGM] atomic */\r
203   markerBrush[8],   /* [HGM] markers */\r
204   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
205 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
206 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
207 static HPEN gridPen = NULL;\r
208 static HPEN highlightPen = NULL;\r
209 static HPEN premovePen = NULL;\r
210 static NPLOGPALETTE pLogPal;\r
211 static BOOL paletteChanged = FALSE;\r
212 static HICON iconWhite, iconBlack, iconCurrent;\r
213 static int doingSizing = FALSE;\r
214 static int lastSizing = 0;\r
215 static int prevStderrPort;\r
216 static HBITMAP userLogo;\r
217 \r
218 static HBITMAP liteBackTexture = NULL;\r
219 static HBITMAP darkBackTexture = NULL;\r
220 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
221 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
222 static int backTextureSquareSize = 0;\r
223 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
224 \r
225 #if __GNUC__ && !defined(_winmajor)\r
226 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
227 #else\r
228 \r
229 #if defined(_winmajor)\r
230 #define oldDialog (_winmajor < 4)\r
231 #else\r
232 #define oldDialog 0\r
233 #endif\r
234 #endif\r
235 \r
236 #define INTERNATIONAL\r
237 \r
238 #ifdef INTERNATIONAL\r
239 #  define _(s) T_(s)\r
240 #  define N_(s) s\r
241 #else\r
242 #  define _(s) s\r
243 #  define N_(s) s\r
244 #  define T_(s) s\r
245 #  define Translate(x, y)\r
246 #  define LoadLanguageFile(s)\r
247 #endif\r
248 \r
249 #ifdef INTERNATIONAL\r
250 \r
251 Boolean barbaric; // flag indicating if translation is needed\r
252 \r
253 // list of item numbers used in each dialog (used to alter language at run time)\r
254 \r
255 #define ABOUTBOX -1  /* not sure why these are needed */\r
256 #define ABOUTBOX2 -1\r
257 \r
258 int dialogItems[][42] = {\r
259 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
260 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
261   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
262 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,\r
263   OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds,\r
264   OPT_Ranget, IDOK, IDCANCEL }, \r
265 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
266   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
267 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
268 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
269   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
270 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
271 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
272   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
273 { ABOUTBOX2, IDC_ChessBoard }, \r
274 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
275   OPT_GameListClose, IDC_GameListDoFilter }, \r
276 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
277 { DLG_Error, IDOK }, \r
278 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
279   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
280 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
281 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
282   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
283   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
284 { DLG_IndexNumber, IDC_Index }, \r
285 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
286 { DLG_TypeInName, IDOK, IDCANCEL }, \r
287 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
288   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
289 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
290   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
291   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
292   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
293   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
294   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
295   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
296 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
297   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
298   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
299   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
300   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
301   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
302   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
303   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
304   GPB_General, GPB_Alarm, OPT_AutoCreate }, \r
305 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
306   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
307   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
308   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
309   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
310   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
311   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
312   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
313 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
314   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
315   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
316   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
317   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
318   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
319   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
320   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
321   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
322 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
323   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
324   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
325   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
326   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
327 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
328 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
329   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
330 { DLG_MoveHistory }, \r
331 { DLG_EvalGraph }, \r
332 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
333 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
334 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
335   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
336   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
337   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
338 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
339   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
340   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
341 { 0 }\r
342 };\r
343 \r
344 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
345 static int lastChecked;\r
346 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
347 extern int tinyLayout;\r
348 extern char * menuBarText[][10];\r
349 \r
350 void\r
351 LoadLanguageFile(char *name)\r
352 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
353     FILE *f;\r
354     int i=0, j=0, n=0, k;\r
355     char buf[MSG_SIZ];\r
356 \r
357     if(!name || name[0] == NULLCHAR) return;\r
358       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
359     appData.language = oldLanguage;\r
360     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
361     if((f = fopen(buf, "r")) == NULL) return;\r
362     while((k = fgetc(f)) != EOF) {\r
363         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
364         languageBuf[i] = k;\r
365         if(k == '\n') {\r
366             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
367                 char *p;\r
368                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
369                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
370                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
371                         english[j] = languageBuf + n + 1; *p = 0;\r
372                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
373 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
374                     }\r
375                 }\r
376             }\r
377             n = i + 1;\r
378         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
379             switch(k) {\r
380               case 'n': k = '\n'; break;\r
381               case 'r': k = '\r'; break;\r
382               case 't': k = '\t'; break;\r
383             }\r
384             languageBuf[--i] = k;\r
385         }\r
386         i++;\r
387     }\r
388     fclose(f);\r
389     barbaric = (j != 0);\r
390     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
391 }\r
392 \r
393 char *\r
394 T_(char *s)\r
395 {   // return the translation of the given string\r
396     // efficiency can be improved a lot...\r
397     int i=0;\r
398     static char buf[MSG_SIZ];\r
399 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
400     if(!barbaric) return s;\r
401     if(!s) return ""; // sanity\r
402     while(english[i]) {\r
403         if(!strcmp(s, english[i])) return foreign[i];\r
404         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
405             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
406             return buf;\r
407         }\r
408         i++;\r
409     }\r
410     return s;\r
411 }\r
412 \r
413 void\r
414 Translate(HWND hDlg, int dialogID)\r
415 {   // translate all text items in the given dialog\r
416     int i=0, j, k;\r
417     char buf[MSG_SIZ], *s;\r
418     if(!barbaric) return;\r
419     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
420     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
421     GetWindowText( hDlg, buf, MSG_SIZ );\r
422     s = T_(buf);\r
423     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
424     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
425         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
426         if(strlen(buf) == 0) continue;\r
427         s = T_(buf);\r
428         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
429     }\r
430 }\r
431 \r
432 HMENU\r
433 TranslateOneMenu(int i, HMENU subMenu)\r
434 {\r
435     int j;\r
436     static MENUITEMINFO info;\r
437 \r
438     info.cbSize = sizeof(MENUITEMINFO);\r
439     info.fMask = MIIM_STATE | MIIM_TYPE;\r
440           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
441             char buf[MSG_SIZ];\r
442             info.dwTypeData = buf;\r
443             info.cch = sizeof(buf);\r
444             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
445             if(i < 10) {\r
446                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
447                 else menuText[i][j] = strdup(buf); // remember original on first change\r
448             }\r
449             if(buf[0] == NULLCHAR) continue;\r
450             info.dwTypeData = T_(buf);\r
451             info.cch = strlen(buf)+1;\r
452             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
453           }\r
454     return subMenu;\r
455 }\r
456 \r
457 void\r
458 TranslateMenus(int addLanguage)\r
459 {\r
460     int i;\r
461     WIN32_FIND_DATA fileData;\r
462     HANDLE hFind;\r
463 #define IDM_English 1970\r
464     if(1) {\r
465         HMENU mainMenu = GetMenu(hwndMain);\r
466         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
467           HMENU subMenu = GetSubMenu(mainMenu, i);\r
468           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
469                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
470           TranslateOneMenu(i, subMenu);\r
471         }\r
472         DrawMenuBar(hwndMain);\r
473     }\r
474 \r
475     if(!addLanguage) return;\r
476     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
477         HMENU mainMenu = GetMenu(hwndMain);\r
478         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
479         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
480         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
481         i = 0; lastChecked = IDM_English;\r
482         do {\r
483             char *p, *q = fileData.cFileName;\r
484             int checkFlag = MF_UNCHECKED;\r
485             languageFile[i] = strdup(q);\r
486             if(barbaric && !strcmp(oldLanguage, q)) {\r
487                 checkFlag = MF_CHECKED;\r
488                 lastChecked = IDM_English + i + 1;\r
489                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
490             }\r
491             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
492             p = strstr(fileData.cFileName, ".lng");\r
493             if(p) *p = 0;\r
494             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
495         } while(FindNextFile(hFind, &fileData));\r
496         FindClose(hFind);\r
497     }\r
498 }\r
499 \r
500 #endif\r
501 \r
502 #define IDM_RecentEngines 3000\r
503 \r
504 void\r
505 RecentEngineMenu (char *s)\r
506 {\r
507     if(appData.icsActive) return;\r
508     if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
509         HMENU mainMenu = GetMenu(hwndMain);\r
510         HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
511         int i=IDM_RecentEngines;\r
512         recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
513         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
514         while(*s) {\r
515           char *p = strchr(s, '\n');\r
516           if(p == NULL) return; // malformed!\r
517           *p = NULLCHAR;\r
518           AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
519           *p = '\n';\r
520           s = p+1;\r
521         }\r
522     }\r
523 }\r
524 \r
525 \r
526 typedef struct {\r
527   char *name;\r
528   int squareSize;\r
529   int lineGap;\r
530   int smallLayout;\r
531   int tinyLayout;\r
532   int cliWidth, cliHeight;\r
533 } SizeInfo;\r
534 \r
535 SizeInfo sizeInfo[] = \r
536 {\r
537   { "tiny",     21, 0, 1, 2, 0, 0 },\r
538   { "teeny",    25, 1, 1, 2, 0, 0 },\r
539   { "dinky",    29, 1, 1, 2, 0, 0 },\r
540   { "petite",   33, 1, 1, 2, 0, 0 },\r
541   { "slim",     37, 2, 1, 1, 0, 0 },\r
542   { "small",    40, 2, 1, 1, 0, 0 },\r
543   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
544   { "middling", 49, 2, 0, 0, 0, 0 },\r
545   { "average",  54, 2, 0, 0, 0, 0 },\r
546   { "moderate", 58, 3, 0, 0, 0, 0 },\r
547   { "medium",   64, 3, 0, 0, 0, 0 },\r
548   { "bulky",    72, 3, 0, 0, 0, 0 },\r
549   { "large",    80, 3, 0, 0, 0, 0 },\r
550   { "big",      87, 3, 0, 0, 0, 0 },\r
551   { "huge",     95, 3, 0, 0, 0, 0 },\r
552   { "giant",    108, 3, 0, 0, 0, 0 },\r
553   { "colossal", 116, 4, 0, 0, 0, 0 },\r
554   { "titanic",  129, 4, 0, 0, 0, 0 },\r
555   { NULL, 0, 0, 0, 0, 0, 0 }\r
556 };\r
557 \r
558 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
559 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
560 {\r
561   { 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
562   { 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
563   { 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
564   { 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
565   { 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
566   { 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
567   { 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
568   { 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
569   { 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
570   { 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
571   { 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
572   { 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
573   { 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
574   { 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
575   { 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
576   { 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
577   { 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
578   { 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
579 };\r
580 \r
581 MyFont *font[NUM_SIZES][NUM_FONTS];\r
582 \r
583 typedef struct {\r
584   char *label;\r
585   int id;\r
586   HWND hwnd;\r
587   WNDPROC wndproc;\r
588 } MyButtonDesc;\r
589 \r
590 #define BUTTON_WIDTH (tinyLayout == 2 ? 16 : 32)\r
591 #define N_BUTTONS 5\r
592 \r
593 MyButtonDesc buttonDesc[N_BUTTONS] =\r
594 {\r
595   {"<<", IDM_ToStart, NULL, NULL},\r
596   {"<", IDM_Backward, NULL, NULL},\r
597   {"P", IDM_Pause, NULL, NULL},\r
598   {">", IDM_Forward, NULL, NULL},\r
599   {">>", IDM_ToEnd, NULL, NULL},\r
600 };\r
601 \r
602 int tinyLayout = 0, smallLayout = 0;\r
603 #define MENU_BAR_ITEMS 9\r
604 char *menuBarText[3][MENU_BAR_ITEMS+1] = {\r
605   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
606   { N_("&Fil"), N_("&Ed"), N_("&Vw"), N_("&Mod"), N_("&Act"), N_("E&ng"), N_("&Opt"), N_("&Hlp"), NULL },\r
607   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
608 };\r
609 \r
610 \r
611 MySound sounds[(int)NSoundClasses];\r
612 MyTextAttribs textAttribs[(int)NColorClasses];\r
613 \r
614 MyColorizeAttribs colorizeAttribs[] = {\r
615   { (COLORREF)0, 0, N_("Shout Text") },\r
616   { (COLORREF)0, 0, N_("SShout/CShout") },\r
617   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
618   { (COLORREF)0, 0, N_("Channel Text") },\r
619   { (COLORREF)0, 0, N_("Kibitz Text") },\r
620   { (COLORREF)0, 0, N_("Tell Text") },\r
621   { (COLORREF)0, 0, N_("Challenge Text") },\r
622   { (COLORREF)0, 0, N_("Request Text") },\r
623   { (COLORREF)0, 0, N_("Seek Text") },\r
624   { (COLORREF)0, 0, N_("Normal Text") },\r
625   { (COLORREF)0, 0, N_("None") }\r
626 };\r
627 \r
628 \r
629 \r
630 static char *commentTitle;\r
631 static char *commentText;\r
632 static int commentIndex;\r
633 static Boolean editComment = FALSE;\r
634 \r
635 \r
636 char errorTitle[MSG_SIZ];\r
637 char errorMessage[2*MSG_SIZ];\r
638 HWND errorDialog = NULL;\r
639 BOOLEAN moveErrorMessageUp = FALSE;\r
640 BOOLEAN consoleEcho = TRUE;\r
641 CHARFORMAT consoleCF;\r
642 COLORREF consoleBackgroundColor;\r
643 \r
644 char *programVersion;\r
645 \r
646 #define CPReal 1\r
647 #define CPComm 2\r
648 #define CPSock 3\r
649 #define CPRcmd 4\r
650 typedef int CPKind;\r
651 \r
652 typedef struct {\r
653   CPKind kind;\r
654   HANDLE hProcess;\r
655   DWORD pid;\r
656   HANDLE hTo;\r
657   HANDLE hFrom;\r
658   SOCKET sock;\r
659   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
660 } ChildProc;\r
661 \r
662 #define INPUT_SOURCE_BUF_SIZE 4096\r
663 \r
664 typedef struct _InputSource {\r
665   CPKind kind;\r
666   HANDLE hFile;\r
667   SOCKET sock;\r
668   int lineByLine;\r
669   HANDLE hThread;\r
670   DWORD id;\r
671   char buf[INPUT_SOURCE_BUF_SIZE];\r
672   char *next;\r
673   DWORD count;\r
674   int error;\r
675   InputCallback func;\r
676   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
677   VOIDSTAR closure;\r
678 } InputSource;\r
679 \r
680 InputSource *consoleInputSource;\r
681 \r
682 DCB dcb;\r
683 \r
684 /* forward */\r
685 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
686 VOID ConsoleCreate();\r
687 LRESULT CALLBACK\r
688   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
689 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
690 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
691 VOID ParseCommSettings(char *arg, DCB *dcb);\r
692 LRESULT CALLBACK\r
693   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
694 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
695 void ParseIcsTextMenu(char *icsTextMenuString);\r
696 VOID PopUpNameDialog(char firstchar);\r
697 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
698 \r
699 /* [AS] */\r
700 int NewGameFRC();\r
701 int GameListOptions();\r
702 \r
703 int dummy; // [HGM] for obsolete args\r
704 \r
705 HWND hwndMain = NULL;        /* root window*/\r
706 HWND hwndConsole = NULL;\r
707 HWND commentDialog = NULL;\r
708 HWND moveHistoryDialog = NULL;\r
709 HWND evalGraphDialog = NULL;\r
710 HWND engineOutputDialog = NULL;\r
711 HWND gameListDialog = NULL;\r
712 HWND editTagsDialog = NULL;\r
713 \r
714 int commentUp = FALSE;\r
715 \r
716 WindowPlacement wpMain;\r
717 WindowPlacement wpConsole;\r
718 WindowPlacement wpComment;\r
719 WindowPlacement wpMoveHistory;\r
720 WindowPlacement wpEvalGraph;\r
721 WindowPlacement wpEngineOutput;\r
722 WindowPlacement wpGameList;\r
723 WindowPlacement wpTags;\r
724 \r
725 VOID EngineOptionsPopup(); // [HGM] settings\r
726 \r
727 VOID GothicPopUp(char *title, VariantClass variant);\r
728 /*\r
729  * Setting "frozen" should disable all user input other than deleting\r
730  * the window.  We do this while engines are initializing themselves.\r
731  */\r
732 static int frozen = 0;\r
733 static int oldMenuItemState[MENU_BAR_ITEMS];\r
734 void FreezeUI()\r
735 {\r
736   HMENU hmenu;\r
737   int i;\r
738 \r
739   if (frozen) return;\r
740   frozen = 1;\r
741   hmenu = GetMenu(hwndMain);\r
742   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
743     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
744   }\r
745   DrawMenuBar(hwndMain);\r
746 }\r
747 \r
748 /* Undo a FreezeUI */\r
749 void ThawUI()\r
750 {\r
751   HMENU hmenu;\r
752   int i;\r
753 \r
754   if (!frozen) return;\r
755   frozen = 0;\r
756   hmenu = GetMenu(hwndMain);\r
757   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
758     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
759   }\r
760   DrawMenuBar(hwndMain);\r
761 }\r
762 \r
763 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
764 \r
765 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
766 #ifdef JAWS\r
767 #include "jaws.c"\r
768 #else\r
769 #define JAWS_INIT\r
770 #define JAWS_ARGS\r
771 #define JAWS_ALT_INTERCEPT\r
772 #define JAWS_KBUP_NAVIGATION\r
773 #define JAWS_KBDOWN_NAVIGATION\r
774 #define JAWS_MENU_ITEMS\r
775 #define JAWS_SILENCE\r
776 #define JAWS_REPLAY\r
777 #define JAWS_ACCEL\r
778 #define JAWS_COPYRIGHT\r
779 #define JAWS_DELETE(X) X\r
780 #define SAYMACHINEMOVE()\r
781 #define SAY(X)\r
782 #endif\r
783 \r
784 /*---------------------------------------------------------------------------*\\r
785  *\r
786  * WinMain\r
787  *\r
788 \*---------------------------------------------------------------------------*/\r
789 \r
790 static void HandleMessage P((MSG *message));\r
791 static HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
792 \r
793 int APIENTRY\r
794 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
795         LPSTR lpCmdLine, int nCmdShow)\r
796 {\r
797   MSG msg;\r
798 //  INITCOMMONCONTROLSEX ex;\r
799 \r
800   debugFP = stderr;\r
801 \r
802   LoadLibrary("RICHED32.DLL");\r
803   consoleCF.cbSize = sizeof(CHARFORMAT);\r
804 \r
805   if (!InitApplication(hInstance)) {\r
806     return (FALSE);\r
807   }\r
808   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
809     return (FALSE);\r
810   }\r
811 \r
812   JAWS_INIT\r
813   TranslateMenus(1);\r
814 \r
815 //  InitCommonControlsEx(&ex);\r
816   InitCommonControls();\r
817 \r
818   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
819   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
820   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
821 \r
822   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
823 \r
824   while (GetMessage(&msg, /* message structure */\r
825                     NULL, /* handle of window receiving the message */\r
826                     0,    /* lowest message to examine */\r
827                     0))   /* highest message to examine */\r
828     {\r
829         HandleMessage(&msg);\r
830     }\r
831 \r
832 \r
833   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
834 }\r
835 \r
836 static void\r
837 HandleMessage (MSG *message)\r
838 {\r
839     MSG msg = *message;\r
840 \r
841       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
842         // [HGM] navigate: switch between all windows with tab\r
843         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
844         int i, currentElement = 0;\r
845 \r
846         // first determine what element of the chain we come from (if any)\r
847         if(appData.icsActive) {\r
848             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
849             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
850         }\r
851         if(engineOutputDialog && EngineOutputIsUp()) {\r
852             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
853             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
854         }\r
855         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
856             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
857         }\r
858         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
859         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
860         if(msg.hwnd == e1)                 currentElement = 2; else\r
861         if(msg.hwnd == e2)                 currentElement = 3; else\r
862         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
863         if(msg.hwnd == mh)                currentElement = 4; else\r
864         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
865         if(msg.hwnd == hText)  currentElement = 5; else\r
866         if(msg.hwnd == hInput) currentElement = 6; else\r
867         for (i = 0; i < N_BUTTONS; i++) {\r
868             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
869         }\r
870 \r
871         // determine where to go to\r
872         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
873           do {\r
874             currentElement = (currentElement + direction) % 7;\r
875             switch(currentElement) {\r
876                 case 0:\r
877                   h = hwndMain; break; // passing this case always makes the loop exit\r
878                 case 1:\r
879                   h = buttonDesc[0].hwnd; break; // could be NULL\r
880                 case 2:\r
881                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
882                   h = e1; break;\r
883                 case 3:\r
884                   if(!EngineOutputIsUp()) continue;\r
885                   h = e2; break;\r
886                 case 4:\r
887                   if(!MoveHistoryIsUp()) continue;\r
888                   h = mh; break;\r
889 //              case 6: // input to eval graph does not seem to get here!\r
890 //                if(!EvalGraphIsUp()) continue;\r
891 //                h = evalGraphDialog; break;\r
892                 case 5:\r
893                   if(!appData.icsActive) continue;\r
894                   SAY("display");\r
895                   h = hText; break;\r
896                 case 6:\r
897                   if(!appData.icsActive) continue;\r
898                   SAY("input");\r
899                   h = hInput; break;\r
900             }\r
901           } while(h == 0);\r
902 \r
903           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
904           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
905           SetFocus(h);\r
906 \r
907           return; // this message now has been processed\r
908         }\r
909       }\r
910 \r
911       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
912           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
913           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
914           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
915           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
916           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
917           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
918           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
919           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
920           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
921         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
922         for(i=0; i<MAX_CHAT; i++) \r
923             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
924                 done = 1; break;\r
925         }\r
926         if(done) return; // [HGM] chat: end patch\r
927         TranslateMessage(&msg); /* Translates virtual key codes */\r
928         DispatchMessage(&msg);  /* Dispatches message to window */\r
929       }\r
930 }\r
931 \r
932 void\r
933 DoEvents ()\r
934 { /* Dispatch pending messages */\r
935   MSG msg;\r
936   while (PeekMessage(&msg, /* message structure */\r
937                      NULL, /* handle of window receiving the message */\r
938                      0,    /* lowest message to examine */\r
939                      0,    /* highest message to examine */\r
940                      PM_REMOVE))\r
941     {\r
942         HandleMessage(&msg);\r
943     }\r
944 }\r
945 \r
946 /*---------------------------------------------------------------------------*\\r
947  *\r
948  * Initialization functions\r
949  *\r
950 \*---------------------------------------------------------------------------*/\r
951 \r
952 void\r
953 SetUserLogo()\r
954 {   // update user logo if necessary\r
955     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
956 \r
957     if(appData.autoLogo) {\r
958           curName = UserName();\r
959           if(strcmp(curName, oldUserName)) {\r
960                 GetCurrentDirectory(MSG_SIZ, dir);\r
961                 SetCurrentDirectory(installDir);\r
962                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
963                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
964                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
965                 if(userLogo == NULL)\r
966                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
967                 SetCurrentDirectory(dir); /* return to prev directory */\r
968           }\r
969     }\r
970 }\r
971 \r
972 BOOL\r
973 InitApplication(HINSTANCE hInstance)\r
974 {\r
975   WNDCLASS wc;\r
976 \r
977   /* Fill in window class structure with parameters that describe the */\r
978   /* main window. */\r
979 \r
980   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
981   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
982   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
983   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
984   wc.hInstance     = hInstance;         /* Owner of this class */\r
985   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
986   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
987   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
988   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
989   wc.lpszClassName = szAppName;                 /* Name to register as */\r
990 \r
991   /* Register the window class and return success/failure code. */\r
992   if (!RegisterClass(&wc)) return FALSE;\r
993 \r
994   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
995   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
996   wc.cbClsExtra    = 0;\r
997   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
998   wc.hInstance     = hInstance;\r
999   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
1000   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
1001   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
1002   wc.lpszMenuName  = NULL;\r
1003   wc.lpszClassName = szConsoleName;\r
1004 \r
1005   if (!RegisterClass(&wc)) return FALSE;\r
1006   return TRUE;\r
1007 }\r
1008 \r
1009 \r
1010 /* Set by InitInstance, used by EnsureOnScreen */\r
1011 int screenHeight, screenWidth;\r
1012 RECT screenGeometry;\r
1013 \r
1014 void\r
1015 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
1016 {\r
1017 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
1018   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
1019   if (*x > screenGeometry.right - 32) *x = screenGeometry.left;\r
1020   if (*y > screenGeometry.bottom - 32) *y = screenGeometry.top;\r
1021   if (*x < screenGeometry.left + minX) *x = screenGeometry.left + minX;\r
1022   if (*y < screenGeometry.top + minY) *y = screenGeometry.top + minY;\r
1023 }\r
1024 \r
1025 VOID\r
1026 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
1027 {\r
1028   char buf[MSG_SIZ], dir[MSG_SIZ];\r
1029   GetCurrentDirectory(MSG_SIZ, dir);\r
1030   SetCurrentDirectory(installDir);\r
1031   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
1032       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1033 \r
1034       if (cps->programLogo == NULL && appData.debugMode) {\r
1035           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1036       }\r
1037   } else if(appData.autoLogo) {\r
1038       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1039         char *opponent = "";\r
1040         if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;\r
1041         if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;\r
1042         sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);\r
1043         if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {\r
1044             sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1045             cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1046         }\r
1047       } else\r
1048       if(appData.directory[n] && appData.directory[n][0]) {\r
1049         SetCurrentDirectory(appData.directory[n]);\r
1050         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1051       }\r
1052   }\r
1053   SetCurrentDirectory(dir); /* return to prev directory */\r
1054 }\r
1055 \r
1056 VOID\r
1057 InitTextures()\r
1058 {\r
1059   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1060   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1061   \r
1062   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1063       if(liteBackTexture) DeleteObject(liteBackTexture);\r
1064       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1065       liteBackTextureMode = appData.liteBackTextureMode;\r
1066 \r
1067       if (liteBackTexture == NULL && appData.debugMode) {\r
1068           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1069       }\r
1070   }\r
1071   \r
1072   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1073       if(darkBackTexture) DeleteObject(darkBackTexture);\r
1074       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1075       darkBackTextureMode = appData.darkBackTextureMode;\r
1076 \r
1077       if (darkBackTexture == NULL && appData.debugMode) {\r
1078           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1079       }\r
1080   }\r
1081 }\r
1082 \r
1083 #ifndef SM_CXVIRTUALSCREEN\r
1084 #define SM_CXVIRTUALSCREEN 78\r
1085 #endif\r
1086 #ifndef SM_CYVIRTUALSCREEN\r
1087 #define SM_CYVIRTUALSCREEN 79\r
1088 #endif\r
1089 #ifndef SM_XVIRTUALSCREEN \r
1090 #define SM_XVIRTUALSCREEN 76\r
1091 #endif\r
1092 #ifndef SM_YVIRTUALSCREEN \r
1093 #define SM_YVIRTUALSCREEN 77\r
1094 #endif\r
1095 \r
1096 VOID\r
1097 InitGeometry()\r
1098 {\r
1099   screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);\r
1100   if( !screenHeight ) screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1101   screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);\r
1102   if( !screenWidth ) screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1103   screenGeometry.left = GetSystemMetrics(SM_XVIRTUALSCREEN);\r
1104   screenGeometry.top = GetSystemMetrics(SM_YVIRTUALSCREEN);\r
1105   screenGeometry.right = screenGeometry.left + screenWidth;\r
1106   screenGeometry.bottom = screenGeometry.top + screenHeight;\r
1107 }\r
1108 \r
1109 BOOL\r
1110 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1111 {\r
1112   HWND hwnd; /* Main window handle. */\r
1113   int ibs;\r
1114   WINDOWPLACEMENT wp;\r
1115   char *filepart;\r
1116 \r
1117   hInst = hInstance;    /* Store instance handle in our global variable */\r
1118   programName = szAppName;\r
1119 \r
1120   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1121     *filepart = NULLCHAR;\r
1122     SetCurrentDirectory(installDir);\r
1123   } else {\r
1124     GetCurrentDirectory(MSG_SIZ, installDir);\r
1125   }\r
1126   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1127   InitGeometry();\r
1128   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1129   /* xboard, and older WinBoards, controlled the move sound with the\r
1130      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1131      always turn the option on (so that the backend will call us),\r
1132      then let the user turn the sound off by setting it to silence if\r
1133      desired.  To accommodate old winboard.ini files saved by old\r
1134      versions of WinBoard, we also turn off the sound if the option\r
1135      was initially set to false. [HGM] taken out of InitAppData */\r
1136   if (!appData.ringBellAfterMoves) {\r
1137     sounds[(int)SoundMove].name = strdup("");\r
1138     appData.ringBellAfterMoves = TRUE;\r
1139   }\r
1140   if (appData.debugMode) {\r
1141     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1142     setbuf(debugFP, NULL);\r
1143   }\r
1144 \r
1145   LoadLanguageFile(appData.language);\r
1146 \r
1147   InitBackEnd1();\r
1148 \r
1149 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1150 //  InitEngineUCI( installDir, &second );\r
1151 \r
1152   /* Create a main window for this application instance. */\r
1153   hwnd = CreateWindow(szAppName, szTitle,\r
1154                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1155                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1156                       NULL, NULL, hInstance, NULL);\r
1157   hwndMain = hwnd;\r
1158 \r
1159   /* If window could not be created, return "failure" */\r
1160   if (!hwnd) {\r
1161     return (FALSE);\r
1162   }\r
1163 \r
1164   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1165   LoadLogo(&first, 0, FALSE);\r
1166   LoadLogo(&second, 1, appData.icsActive);\r
1167 \r
1168   SetUserLogo();\r
1169 \r
1170   iconWhite = LoadIcon(hInstance, "icon_white");\r
1171   iconBlack = LoadIcon(hInstance, "icon_black");\r
1172   iconCurrent = iconWhite;\r
1173   InitDrawingColors();\r
1174 \r
1175   InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args\r
1176   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1177     /* Compute window size for each board size, and use the largest\r
1178        size that fits on this screen as the default. */\r
1179     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1180     if (boardSize == (BoardSize)-1 &&\r
1181         winH <= screenHeight\r
1182            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1183         && winW <= screenWidth) {\r
1184       boardSize = (BoardSize)ibs;\r
1185     }\r
1186   }\r
1187 \r
1188   InitDrawingSizes(boardSize, 0);\r
1189   RecentEngineMenu(appData.recentEngineList);\r
1190   InitMenuChecks();\r
1191   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1192 \r
1193   /* [AS] Load textures if specified */\r
1194   InitTextures();\r
1195 \r
1196   mysrandom( (unsigned) time(NULL) );\r
1197 \r
1198   /* [AS] Restore layout */\r
1199   if( wpMoveHistory.visible ) {\r
1200       MoveHistoryPopUp();\r
1201   }\r
1202 \r
1203   if( wpEvalGraph.visible ) {\r
1204       EvalGraphPopUp();\r
1205   }\r
1206 \r
1207   if( wpEngineOutput.visible ) {\r
1208       EngineOutputPopUp();\r
1209   }\r
1210 \r
1211   /* Make the window visible; update its client area; and return "success" */\r
1212   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1213   wp.length = sizeof(WINDOWPLACEMENT);\r
1214   wp.flags = 0;\r
1215   wp.showCmd = nCmdShow;\r
1216   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1217   wp.rcNormalPosition.left = wpMain.x;\r
1218   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1219   wp.rcNormalPosition.top = wpMain.y;\r
1220   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1221   SetWindowPlacement(hwndMain, &wp);\r
1222 \r
1223   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1224 \r
1225   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1226                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1227 \r
1228   if (hwndConsole) {\r
1229 #if AOT_CONSOLE\r
1230     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1231                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1232 #endif\r
1233     ShowWindow(hwndConsole, nCmdShow);\r
1234     SetActiveWindow(hwndConsole);\r
1235   }\r
1236   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1237   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1238 \r
1239   return TRUE;\r
1240 \r
1241 }\r
1242 \r
1243 VOID\r
1244 InitMenuChecks()\r
1245 {\r
1246   HMENU hmenu = GetMenu(hwndMain);\r
1247 \r
1248   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1249                         MF_BYCOMMAND|((appData.icsActive &&\r
1250                                        *appData.icsCommPort != NULLCHAR) ?\r
1251                                       MF_ENABLED : MF_GRAYED));\r
1252   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1253                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1254                                      MF_CHECKED : MF_UNCHECKED));\r
1255   EnableMenuItem(hmenu, IDM_SaveSelected, MF_GRAYED);\r
1256 }\r
1257 \r
1258 //---------------------------------------------------------------------------------------------------------\r
1259 \r
1260 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1261 #define XBOARD FALSE\r
1262 \r
1263 #define OPTCHAR "/"\r
1264 #define SEPCHAR "="\r
1265 #define TOPLEVEL 0\r
1266 \r
1267 #include "args.h"\r
1268 \r
1269 // front-end part of option handling\r
1270 \r
1271 VOID\r
1272 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1273 {\r
1274   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1275   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1276   DeleteDC(hdc);\r
1277   lf->lfWidth = 0;\r
1278   lf->lfEscapement = 0;\r
1279   lf->lfOrientation = 0;\r
1280   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1281   lf->lfItalic = mfp->italic;\r
1282   lf->lfUnderline = mfp->underline;\r
1283   lf->lfStrikeOut = mfp->strikeout;\r
1284   lf->lfCharSet = mfp->charset;\r
1285   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1286 \r
1287 \r
1288 \r
1289   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1290   lf->lfQuality = DEFAULT_QUALITY;\r
1291   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1292     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1293 }\r
1294 \r
1295 void\r
1296 CreateFontInMF(MyFont *mf)\r
1297\r
1298   LFfromMFP(&mf->lf, &mf->mfp);\r
1299   if (mf->hf) DeleteObject(mf->hf);\r
1300   mf->hf = CreateFontIndirect(&mf->lf);\r
1301 }\r
1302 \r
1303 // [HGM] This platform-dependent table provides the location for storing the color info\r
1304 void *\r
1305 colorVariable[] = {\r
1306   &whitePieceColor, \r
1307   &blackPieceColor, \r
1308   &lightSquareColor,\r
1309   &darkSquareColor, \r
1310   &highlightSquareColor,\r
1311   &premoveHighlightColor,\r
1312   NULL,\r
1313   &consoleBackgroundColor,\r
1314   &appData.fontForeColorWhite,\r
1315   &appData.fontBackColorWhite,\r
1316   &appData.fontForeColorBlack,\r
1317   &appData.fontBackColorBlack,\r
1318   &appData.evalHistColorWhite,\r
1319   &appData.evalHistColorBlack,\r
1320   &appData.highlightArrowColor,\r
1321 };\r
1322 \r
1323 /* Command line font name parser.  NULL name means do nothing.\r
1324    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1325    For backward compatibility, syntax without the colon is also\r
1326    accepted, but font names with digits in them won't work in that case.\r
1327 */\r
1328 VOID\r
1329 ParseFontName(char *name, MyFontParams *mfp)\r
1330 {\r
1331   char *p, *q;\r
1332   if (name == NULL) return;\r
1333   p = name;\r
1334   q = strchr(p, ':');\r
1335   if (q) {\r
1336     if (q - p >= sizeof(mfp->faceName))\r
1337       ExitArgError(_("Font name too long:"), name, TRUE);\r
1338     memcpy(mfp->faceName, p, q - p);\r
1339     mfp->faceName[q - p] = NULLCHAR;\r
1340     p = q + 1;\r
1341   } else {\r
1342     q = mfp->faceName;\r
1343 \r
1344     while (*p && !isdigit(*p)) {\r
1345       *q++ = *p++;\r
1346       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1347         ExitArgError(_("Font name too long:"), name, TRUE);\r
1348     }\r
1349     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1350     *q = NULLCHAR;\r
1351   }\r
1352   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1353   mfp->pointSize = (float) atof(p);\r
1354   mfp->bold = (strchr(p, 'b') != NULL);\r
1355   mfp->italic = (strchr(p, 'i') != NULL);\r
1356   mfp->underline = (strchr(p, 'u') != NULL);\r
1357   mfp->strikeout = (strchr(p, 's') != NULL);\r
1358   mfp->charset = DEFAULT_CHARSET;\r
1359   q = strchr(p, 'c');\r
1360   if (q)\r
1361     mfp->charset = (BYTE) atoi(q+1);\r
1362 }\r
1363 \r
1364 void\r
1365 ParseFont(char *name, int number)\r
1366 { // wrapper to shield back-end from 'font'\r
1367   ParseFontName(name, &font[boardSize][number]->mfp);\r
1368 }\r
1369 \r
1370 void\r
1371 SetFontDefaults()\r
1372 { // in WB  we have a 2D array of fonts; this initializes their description\r
1373   int i, j;\r
1374   /* Point font array elements to structures and\r
1375      parse default font names */\r
1376   for (i=0; i<NUM_FONTS; i++) {\r
1377     for (j=0; j<NUM_SIZES; j++) {\r
1378       font[j][i] = &fontRec[j][i];\r
1379       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1380     }\r
1381   }\r
1382 }\r
1383 \r
1384 void\r
1385 CreateFonts()\r
1386 { // here we create the actual fonts from the selected descriptions\r
1387   int i, j;\r
1388   for (i=0; i<NUM_FONTS; i++) {\r
1389     for (j=0; j<NUM_SIZES; j++) {\r
1390       CreateFontInMF(font[j][i]);\r
1391     }\r
1392   }\r
1393 }\r
1394 /* Color name parser.\r
1395    X version accepts X color names, but this one\r
1396    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1397 COLORREF\r
1398 ParseColorName(char *name)\r
1399 {\r
1400   int red, green, blue, count;\r
1401   char buf[MSG_SIZ];\r
1402 \r
1403   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1404   if (count != 3) {\r
1405     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1406       &red, &green, &blue);\r
1407   }\r
1408   if (count != 3) {\r
1409     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1410     DisplayError(buf, 0);\r
1411     return RGB(0, 0, 0);\r
1412   }\r
1413   return PALETTERGB(red, green, blue);\r
1414 }\r
1415 \r
1416 void\r
1417 ParseColor(int n, char *name)\r
1418 { // for WinBoard the color is an int, which needs to be derived from the string\r
1419   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1420 }\r
1421 \r
1422 void\r
1423 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1424 {\r
1425   char *e = argValue;\r
1426   int eff = 0;\r
1427 \r
1428   while (*e) {\r
1429     if (*e == 'b')      eff |= CFE_BOLD;\r
1430     else if (*e == 'i') eff |= CFE_ITALIC;\r
1431     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1432     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1433     else if (*e == '#' || isdigit(*e)) break;\r
1434     e++;\r
1435   }\r
1436   *effects = eff;\r
1437   *color   = ParseColorName(e);\r
1438 }\r
1439 \r
1440 void\r
1441 ParseTextAttribs(ColorClass cc, char *s)\r
1442 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1443     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1444     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1445 }\r
1446 \r
1447 void\r
1448 ParseBoardSize(void *addr, char *name)\r
1449 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1450   BoardSize bs = SizeTiny;\r
1451   while (sizeInfo[bs].name != NULL) {\r
1452     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1453         *(BoardSize *)addr = bs;\r
1454         return;\r
1455     }\r
1456     bs++;\r
1457   }\r
1458   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1459 }\r
1460 \r
1461 void\r
1462 LoadAllSounds()\r
1463 { // [HGM] import name from appData first\r
1464   ColorClass cc;\r
1465   SoundClass sc;\r
1466   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1467     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1468     textAttribs[cc].sound.data = NULL;\r
1469     MyLoadSound(&textAttribs[cc].sound);\r
1470   }\r
1471   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1472     textAttribs[cc].sound.name = strdup("");\r
1473     textAttribs[cc].sound.data = NULL;\r
1474   }\r
1475   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1476     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1477     sounds[sc].data = NULL;\r
1478     MyLoadSound(&sounds[sc]);\r
1479   }\r
1480 }\r
1481 \r
1482 void\r
1483 SetCommPortDefaults()\r
1484 {\r
1485    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1486   dcb.DCBlength = sizeof(DCB);\r
1487   dcb.BaudRate = 9600;\r
1488   dcb.fBinary = TRUE;\r
1489   dcb.fParity = FALSE;\r
1490   dcb.fOutxCtsFlow = FALSE;\r
1491   dcb.fOutxDsrFlow = FALSE;\r
1492   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1493   dcb.fDsrSensitivity = FALSE;\r
1494   dcb.fTXContinueOnXoff = TRUE;\r
1495   dcb.fOutX = FALSE;\r
1496   dcb.fInX = FALSE;\r
1497   dcb.fNull = FALSE;\r
1498   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1499   dcb.fAbortOnError = FALSE;\r
1500   dcb.ByteSize = 7;\r
1501   dcb.Parity = SPACEPARITY;\r
1502   dcb.StopBits = ONESTOPBIT;\r
1503 }\r
1504 \r
1505 // [HGM] args: these three cases taken out to stay in front-end\r
1506 void\r
1507 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1508 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1509         // while the curent board size determines the element. This system should be ported to XBoard.\r
1510         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1511         int bs;\r
1512         for (bs=0; bs<NUM_SIZES; bs++) {\r
1513           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1514           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1515           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1516             ad->argName, mfp->faceName, mfp->pointSize,\r
1517             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1518             mfp->bold ? "b" : "",\r
1519             mfp->italic ? "i" : "",\r
1520             mfp->underline ? "u" : "",\r
1521             mfp->strikeout ? "s" : "",\r
1522             (int)mfp->charset);\r
1523         }\r
1524       }\r
1525 \r
1526 void\r
1527 ExportSounds()\r
1528 { // [HGM] copy the names from the internal WB variables to appData\r
1529   ColorClass cc;\r
1530   SoundClass sc;\r
1531   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1532     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1533   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1534     (&appData.soundMove)[sc] = sounds[sc].name;\r
1535 }\r
1536 \r
1537 void\r
1538 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1539 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1540         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1541         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1542           (ta->effects & CFE_BOLD) ? "b" : "",\r
1543           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1544           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1545           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1546           (ta->effects) ? " " : "",\r
1547           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1548       }\r
1549 \r
1550 void\r
1551 SaveColor(FILE *f, ArgDescriptor *ad)\r
1552 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1553         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1554         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1555           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1556 }\r
1557 \r
1558 void\r
1559 SaveBoardSize(FILE *f, char *name, void *addr)\r
1560 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1561   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1562 }\r
1563 \r
1564 void\r
1565 ParseCommPortSettings(char *s)\r
1566 { // wrapper to keep dcb from back-end\r
1567   ParseCommSettings(s, &dcb);\r
1568 }\r
1569 \r
1570 void\r
1571 GetWindowCoords()\r
1572 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1573   GetActualPlacement(hwndMain, &wpMain);\r
1574   GetActualPlacement(hwndConsole, &wpConsole);\r
1575   GetActualPlacement(commentDialog, &wpComment);\r
1576   GetActualPlacement(editTagsDialog, &wpTags);\r
1577   GetActualPlacement(gameListDialog, &wpGameList);\r
1578   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1579   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1580   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1581 }\r
1582 \r
1583 void\r
1584 PrintCommPortSettings(FILE *f, char *name)\r
1585 { // wrapper to shield back-end from DCB\r
1586       PrintCommSettings(f, name, &dcb);\r
1587 }\r
1588 \r
1589 int\r
1590 MySearchPath(char *installDir, char *name, char *fullname)\r
1591 {\r
1592   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1593   if(name[0]== '%') {\r
1594     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1595     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1596       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1597       *strchr(buf, '%') = 0;\r
1598       strcat(fullname, getenv(buf));\r
1599       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1600     }\r
1601     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1602     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1603     return (int) strlen(fullname);\r
1604   }\r
1605   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1606 }\r
1607 \r
1608 int\r
1609 MyGetFullPathName(char *name, char *fullname)\r
1610 {\r
1611   char *dummy;\r
1612   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1613 }\r
1614 \r
1615 int\r
1616 MainWindowUp()\r
1617 { // [HGM] args: allows testing if main window is realized from back-end\r
1618   return hwndMain != NULL;\r
1619 }\r
1620 \r
1621 void\r
1622 PopUpStartupDialog()\r
1623 {\r
1624     FARPROC lpProc;\r
1625     \r
1626     LoadLanguageFile(appData.language);\r
1627     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1628     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1629     FreeProcInstance(lpProc);\r
1630 }\r
1631 \r
1632 /*---------------------------------------------------------------------------*\\r
1633  *\r
1634  * GDI board drawing routines\r
1635  *\r
1636 \*---------------------------------------------------------------------------*/\r
1637 \r
1638 /* [AS] Draw square using background texture */\r
1639 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1640 {\r
1641     XFORM   x;\r
1642 \r
1643     if( mode == 0 ) {\r
1644         return; /* Should never happen! */\r
1645     }\r
1646 \r
1647     SetGraphicsMode( dst, GM_ADVANCED );\r
1648 \r
1649     switch( mode ) {\r
1650     case 1:\r
1651         /* Identity */\r
1652         break;\r
1653     case 2:\r
1654         /* X reflection */\r
1655         x.eM11 = -1.0;\r
1656         x.eM12 = 0;\r
1657         x.eM21 = 0;\r
1658         x.eM22 = 1.0;\r
1659         x.eDx = (FLOAT) dw + dx - 1;\r
1660         x.eDy = 0;\r
1661         dx = 0;\r
1662         SetWorldTransform( dst, &x );\r
1663         break;\r
1664     case 3:\r
1665         /* Y reflection */\r
1666         x.eM11 = 1.0;\r
1667         x.eM12 = 0;\r
1668         x.eM21 = 0;\r
1669         x.eM22 = -1.0;\r
1670         x.eDx = 0;\r
1671         x.eDy = (FLOAT) dh + dy - 1;\r
1672         dy = 0;\r
1673         SetWorldTransform( dst, &x );\r
1674         break;\r
1675     case 4:\r
1676         /* X/Y flip */\r
1677         x.eM11 = 0;\r
1678         x.eM12 = 1.0;\r
1679         x.eM21 = 1.0;\r
1680         x.eM22 = 0;\r
1681         x.eDx = (FLOAT) dx;\r
1682         x.eDy = (FLOAT) dy;\r
1683         dx = 0;\r
1684         dy = 0;\r
1685         SetWorldTransform( dst, &x );\r
1686         break;\r
1687     }\r
1688 \r
1689     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1690 \r
1691     x.eM11 = 1.0;\r
1692     x.eM12 = 0;\r
1693     x.eM21 = 0;\r
1694     x.eM22 = 1.0;\r
1695     x.eDx = 0;\r
1696     x.eDy = 0;\r
1697     SetWorldTransform( dst, &x );\r
1698 \r
1699     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1700 }\r
1701 \r
1702 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1703 enum {\r
1704     PM_WP = (int) WhitePawn, \r
1705     PM_WN = (int) WhiteKnight, \r
1706     PM_WB = (int) WhiteBishop, \r
1707     PM_WR = (int) WhiteRook, \r
1708     PM_WQ = (int) WhiteQueen, \r
1709     PM_WF = (int) WhiteFerz, \r
1710     PM_WW = (int) WhiteWazir, \r
1711     PM_WE = (int) WhiteAlfil, \r
1712     PM_WM = (int) WhiteMan, \r
1713     PM_WO = (int) WhiteCannon, \r
1714     PM_WU = (int) WhiteUnicorn, \r
1715     PM_WH = (int) WhiteNightrider, \r
1716     PM_WA = (int) WhiteAngel, \r
1717     PM_WC = (int) WhiteMarshall, \r
1718     PM_WAB = (int) WhiteCardinal, \r
1719     PM_WD = (int) WhiteDragon, \r
1720     PM_WL = (int) WhiteLance, \r
1721     PM_WS = (int) WhiteCobra, \r
1722     PM_WV = (int) WhiteFalcon, \r
1723     PM_WSG = (int) WhiteSilver, \r
1724     PM_WG = (int) WhiteGrasshopper, \r
1725     PM_WK = (int) WhiteKing,\r
1726     PM_BP = (int) BlackPawn, \r
1727     PM_BN = (int) BlackKnight, \r
1728     PM_BB = (int) BlackBishop, \r
1729     PM_BR = (int) BlackRook, \r
1730     PM_BQ = (int) BlackQueen, \r
1731     PM_BF = (int) BlackFerz, \r
1732     PM_BW = (int) BlackWazir, \r
1733     PM_BE = (int) BlackAlfil, \r
1734     PM_BM = (int) BlackMan,\r
1735     PM_BO = (int) BlackCannon, \r
1736     PM_BU = (int) BlackUnicorn, \r
1737     PM_BH = (int) BlackNightrider, \r
1738     PM_BA = (int) BlackAngel, \r
1739     PM_BC = (int) BlackMarshall, \r
1740     PM_BG = (int) BlackGrasshopper, \r
1741     PM_BAB = (int) BlackCardinal,\r
1742     PM_BD = (int) BlackDragon,\r
1743     PM_BL = (int) BlackLance,\r
1744     PM_BS = (int) BlackCobra,\r
1745     PM_BV = (int) BlackFalcon,\r
1746     PM_BSG = (int) BlackSilver,\r
1747     PM_BK = (int) BlackKing\r
1748 };\r
1749 \r
1750 static HFONT hPieceFont = NULL;\r
1751 static HBITMAP hPieceMask[(int) EmptySquare];\r
1752 static HBITMAP hPieceFace[(int) EmptySquare];\r
1753 static int fontBitmapSquareSize = 0;\r
1754 static char pieceToFontChar[(int) EmptySquare] =\r
1755                               { 'p', 'n', 'b', 'r', 'q', \r
1756                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1757                       'k', 'o', 'm', 'v', 't', 'w', \r
1758                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1759                                                               'l' };\r
1760 \r
1761 extern BOOL SetCharTable( char *table, const char * map );\r
1762 /* [HGM] moved to backend.c */\r
1763 \r
1764 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1765 {\r
1766     HBRUSH hbrush;\r
1767     BYTE r1 = GetRValue( color );\r
1768     BYTE g1 = GetGValue( color );\r
1769     BYTE b1 = GetBValue( color );\r
1770     BYTE r2 = r1 / 2;\r
1771     BYTE g2 = g1 / 2;\r
1772     BYTE b2 = b1 / 2;\r
1773     RECT rc;\r
1774 \r
1775     /* Create a uniform background first */\r
1776     hbrush = CreateSolidBrush( color );\r
1777     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1778     FillRect( hdc, &rc, hbrush );\r
1779     DeleteObject( hbrush );\r
1780     \r
1781     if( mode == 1 ) {\r
1782         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1783         int steps = squareSize / 2;\r
1784         int i;\r
1785 \r
1786         for( i=0; i<steps; i++ ) {\r
1787             BYTE r = r1 - (r1-r2) * i / steps;\r
1788             BYTE g = g1 - (g1-g2) * i / steps;\r
1789             BYTE b = b1 - (b1-b2) * i / steps;\r
1790 \r
1791             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1792             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1793             FillRect( hdc, &rc, hbrush );\r
1794             DeleteObject(hbrush);\r
1795         }\r
1796     }\r
1797     else if( mode == 2 ) {\r
1798         /* Diagonal gradient, good more or less for every piece */\r
1799         POINT triangle[3];\r
1800         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1801         HBRUSH hbrush_old;\r
1802         int steps = squareSize;\r
1803         int i;\r
1804 \r
1805         triangle[0].x = squareSize - steps;\r
1806         triangle[0].y = squareSize;\r
1807         triangle[1].x = squareSize;\r
1808         triangle[1].y = squareSize;\r
1809         triangle[2].x = squareSize;\r
1810         triangle[2].y = squareSize - steps;\r
1811 \r
1812         for( i=0; i<steps; i++ ) {\r
1813             BYTE r = r1 - (r1-r2) * i / steps;\r
1814             BYTE g = g1 - (g1-g2) * i / steps;\r
1815             BYTE b = b1 - (b1-b2) * i / steps;\r
1816 \r
1817             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1818             hbrush_old = SelectObject( hdc, hbrush );\r
1819             Polygon( hdc, triangle, 3 );\r
1820             SelectObject( hdc, hbrush_old );\r
1821             DeleteObject(hbrush);\r
1822             triangle[0].x++;\r
1823             triangle[2].y++;\r
1824         }\r
1825 \r
1826         SelectObject( hdc, hpen );\r
1827     }\r
1828 }\r
1829 \r
1830 /*\r
1831     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1832     seems to work ok. The main problem here is to find the "inside" of a chess\r
1833     piece: follow the steps as explained below.\r
1834 */\r
1835 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1836 {\r
1837     HBITMAP hbm;\r
1838     HBITMAP hbm_old;\r
1839     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1840     RECT rc;\r
1841     SIZE sz;\r
1842 \r
1843 \r
1844     POINT pt;\r
1845     int backColor = whitePieceColor; \r
1846     int foreColor = blackPieceColor;\r
1847     \r
1848     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1849         backColor = appData.fontBackColorWhite;\r
1850         foreColor = appData.fontForeColorWhite;\r
1851     }\r
1852     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1853         backColor = appData.fontBackColorBlack;\r
1854         foreColor = appData.fontForeColorBlack;\r
1855     }\r
1856 \r
1857     /* Mask */\r
1858     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1859 \r
1860     hbm_old = SelectObject( hdc, hbm );\r
1861 \r
1862     rc.left = 0;\r
1863     rc.top = 0;\r
1864     rc.right = squareSize;\r
1865     rc.bottom = squareSize;\r
1866 \r
1867     /* Step 1: background is now black */\r
1868     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1869 \r
1870     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1871 \r
1872     pt.x = (squareSize - sz.cx) / 2;\r
1873     pt.y = (squareSize - sz.cy) / 2;\r
1874 \r
1875     SetBkMode( hdc, TRANSPARENT );\r
1876     SetTextColor( hdc, chroma );\r
1877     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1878     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1879 \r
1880     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1881     /* Step 3: the area outside the piece is filled with white */\r
1882 //    FloodFill( hdc, 0, 0, chroma );\r
1883     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1884     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1885     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1886     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1887     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1888     /* \r
1889         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1890         but if the start point is not inside the piece we're lost!\r
1891         There should be a better way to do this... if we could create a region or path\r
1892         from the fill operation we would be fine for example.\r
1893     */\r
1894 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1895     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1896 \r
1897     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1898         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1899         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1900 \r
1901         SelectObject( dc2, bm2 );\r
1902         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1903         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1904         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1905         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1906         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1907 \r
1908         DeleteDC( dc2 );\r
1909         DeleteObject( bm2 );\r
1910     }\r
1911 \r
1912     SetTextColor( hdc, 0 );\r
1913     /* \r
1914         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1915         draw the piece again in black for safety.\r
1916     */\r
1917     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1918 \r
1919     SelectObject( hdc, hbm_old );\r
1920 \r
1921     if( hPieceMask[index] != NULL ) {\r
1922         DeleteObject( hPieceMask[index] );\r
1923     }\r
1924 \r
1925     hPieceMask[index] = hbm;\r
1926 \r
1927     /* Face */\r
1928     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1929 \r
1930     SelectObject( hdc, hbm );\r
1931 \r
1932     {\r
1933         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1934         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1935         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1936 \r
1937         SelectObject( dc1, hPieceMask[index] );\r
1938         SelectObject( dc2, bm2 );\r
1939         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1940         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1941         \r
1942         /* \r
1943             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1944             the piece background and deletes (makes transparent) the rest.\r
1945             Thanks to that mask, we are free to paint the background with the greates\r
1946             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1947             We use this, to make gradients and give the pieces a "roundish" look.\r
1948         */\r
1949         SetPieceBackground( hdc, backColor, 2 );\r
1950         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1951 \r
1952         DeleteDC( dc2 );\r
1953         DeleteDC( dc1 );\r
1954         DeleteObject( bm2 );\r
1955     }\r
1956 \r
1957     SetTextColor( hdc, foreColor );\r
1958     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1959 \r
1960     SelectObject( hdc, hbm_old );\r
1961 \r
1962     if( hPieceFace[index] != NULL ) {\r
1963         DeleteObject( hPieceFace[index] );\r
1964     }\r
1965 \r
1966     hPieceFace[index] = hbm;\r
1967 }\r
1968 \r
1969 static int TranslatePieceToFontPiece( int piece )\r
1970 {\r
1971     switch( piece ) {\r
1972     case BlackPawn:\r
1973         return PM_BP;\r
1974     case BlackKnight:\r
1975         return PM_BN;\r
1976     case BlackBishop:\r
1977         return PM_BB;\r
1978     case BlackRook:\r
1979         return PM_BR;\r
1980     case BlackQueen:\r
1981         return PM_BQ;\r
1982     case BlackKing:\r
1983         return PM_BK;\r
1984     case WhitePawn:\r
1985         return PM_WP;\r
1986     case WhiteKnight:\r
1987         return PM_WN;\r
1988     case WhiteBishop:\r
1989         return PM_WB;\r
1990     case WhiteRook:\r
1991         return PM_WR;\r
1992     case WhiteQueen:\r
1993         return PM_WQ;\r
1994     case WhiteKing:\r
1995         return PM_WK;\r
1996 \r
1997     case BlackAngel:\r
1998         return PM_BA;\r
1999     case BlackMarshall:\r
2000         return PM_BC;\r
2001     case BlackFerz:\r
2002         return PM_BF;\r
2003     case BlackNightrider:\r
2004         return PM_BH;\r
2005     case BlackAlfil:\r
2006         return PM_BE;\r
2007     case BlackWazir:\r
2008         return PM_BW;\r
2009     case BlackUnicorn:\r
2010         return PM_BU;\r
2011     case BlackCannon:\r
2012         return PM_BO;\r
2013     case BlackGrasshopper:\r
2014         return PM_BG;\r
2015     case BlackMan:\r
2016         return PM_BM;\r
2017     case BlackSilver:\r
2018         return PM_BSG;\r
2019     case BlackLance:\r
2020         return PM_BL;\r
2021     case BlackFalcon:\r
2022         return PM_BV;\r
2023     case BlackCobra:\r
2024         return PM_BS;\r
2025     case BlackCardinal:\r
2026         return PM_BAB;\r
2027     case BlackDragon:\r
2028         return PM_BD;\r
2029 \r
2030     case WhiteAngel:\r
2031         return PM_WA;\r
2032     case WhiteMarshall:\r
2033         return PM_WC;\r
2034     case WhiteFerz:\r
2035         return PM_WF;\r
2036     case WhiteNightrider:\r
2037         return PM_WH;\r
2038     case WhiteAlfil:\r
2039         return PM_WE;\r
2040     case WhiteWazir:\r
2041         return PM_WW;\r
2042     case WhiteUnicorn:\r
2043         return PM_WU;\r
2044     case WhiteCannon:\r
2045         return PM_WO;\r
2046     case WhiteGrasshopper:\r
2047         return PM_WG;\r
2048     case WhiteMan:\r
2049         return PM_WM;\r
2050     case WhiteSilver:\r
2051         return PM_WSG;\r
2052     case WhiteLance:\r
2053         return PM_WL;\r
2054     case WhiteFalcon:\r
2055         return PM_WV;\r
2056     case WhiteCobra:\r
2057         return PM_WS;\r
2058     case WhiteCardinal:\r
2059         return PM_WAB;\r
2060     case WhiteDragon:\r
2061         return PM_WD;\r
2062     }\r
2063 \r
2064     return 0;\r
2065 }\r
2066 \r
2067 void CreatePiecesFromFont()\r
2068 {\r
2069     LOGFONT lf;\r
2070     HDC hdc_window = NULL;\r
2071     HDC hdc = NULL;\r
2072     HFONT hfont_old;\r
2073     int fontHeight;\r
2074     int i;\r
2075 \r
2076     if( fontBitmapSquareSize < 0 ) {\r
2077         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2078         return;\r
2079     }\r
2080 \r
2081     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2082             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2083         fontBitmapSquareSize = -1;\r
2084         return;\r
2085     }\r
2086 \r
2087     if( fontBitmapSquareSize != squareSize ) {\r
2088         hdc_window = GetDC( hwndMain );\r
2089         hdc = CreateCompatibleDC( hdc_window );\r
2090 \r
2091         if( hPieceFont != NULL ) {\r
2092             DeleteObject( hPieceFont );\r
2093         }\r
2094         else {\r
2095             for( i=0; i<=(int)BlackKing; i++ ) {\r
2096                 hPieceMask[i] = NULL;\r
2097                 hPieceFace[i] = NULL;\r
2098             }\r
2099         }\r
2100 \r
2101         fontHeight = 75;\r
2102 \r
2103         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2104             fontHeight = appData.fontPieceSize;\r
2105         }\r
2106 \r
2107         fontHeight = (fontHeight * squareSize) / 100;\r
2108 \r
2109         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2110         lf.lfWidth = 0;\r
2111         lf.lfEscapement = 0;\r
2112         lf.lfOrientation = 0;\r
2113         lf.lfWeight = FW_NORMAL;\r
2114         lf.lfItalic = 0;\r
2115         lf.lfUnderline = 0;\r
2116         lf.lfStrikeOut = 0;\r
2117         lf.lfCharSet = DEFAULT_CHARSET;\r
2118         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2119         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2120         lf.lfQuality = PROOF_QUALITY;\r
2121         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2122         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2123         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2124 \r
2125         hPieceFont = CreateFontIndirect( &lf );\r
2126 \r
2127         if( hPieceFont == NULL ) {\r
2128             fontBitmapSquareSize = -2;\r
2129         }\r
2130         else {\r
2131             /* Setup font-to-piece character table */\r
2132             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2133                 /* No (or wrong) global settings, try to detect the font */\r
2134                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2135                     /* Alpha */\r
2136                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2137                 }\r
2138                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2139                     /* DiagramTT* family */\r
2140                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2141                 }\r
2142                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2143                     /* Fairy symbols */\r
2144                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2145                 }\r
2146                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2147                     /* Good Companion (Some characters get warped as literal :-( */\r
2148                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2149                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2150                     SetCharTable(pieceToFontChar, s);\r
2151                 }\r
2152                 else {\r
2153                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2154                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2155                 }\r
2156             }\r
2157 \r
2158             /* Create bitmaps */\r
2159             hfont_old = SelectObject( hdc, hPieceFont );\r
2160             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2161                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2162                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2163 \r
2164             SelectObject( hdc, hfont_old );\r
2165 \r
2166             fontBitmapSquareSize = squareSize;\r
2167         }\r
2168     }\r
2169 \r
2170     if( hdc != NULL ) {\r
2171         DeleteDC( hdc );\r
2172     }\r
2173 \r
2174     if( hdc_window != NULL ) {\r
2175         ReleaseDC( hwndMain, hdc_window );\r
2176     }\r
2177 }\r
2178 \r
2179 HBITMAP\r
2180 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2181 {\r
2182   char name[128], buf[MSG_SIZ];\r
2183 \r
2184     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2185   if(appData.pieceDirectory[0]) {\r
2186     HBITMAP res;\r
2187     snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);\r
2188     res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
2189     if(res) return res;\r
2190   }\r
2191   if (gameInfo.event &&\r
2192       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2193       strcmp(name, "k80s") == 0) {\r
2194     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2195   }\r
2196   return LoadBitmap(hinst, name);\r
2197 }\r
2198 \r
2199 \r
2200 /* Insert a color into the program's logical palette\r
2201    structure.  This code assumes the given color is\r
2202    the result of the RGB or PALETTERGB macro, and it\r
2203    knows how those macros work (which is documented).\r
2204 */\r
2205 VOID\r
2206 InsertInPalette(COLORREF color)\r
2207 {\r
2208   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2209 \r
2210   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2211     DisplayFatalError(_("Too many colors"), 0, 1);\r
2212     pLogPal->palNumEntries--;\r
2213     return;\r
2214   }\r
2215 \r
2216   pe->peFlags = (char) 0;\r
2217   pe->peRed = (char) (0xFF & color);\r
2218   pe->peGreen = (char) (0xFF & (color >> 8));\r
2219   pe->peBlue = (char) (0xFF & (color >> 16));\r
2220   return;\r
2221 }\r
2222 \r
2223 \r
2224 VOID\r
2225 InitDrawingColors()\r
2226 {\r
2227   int i;\r
2228   if (pLogPal == NULL) {\r
2229     /* Allocate enough memory for a logical palette with\r
2230      * PALETTESIZE entries and set the size and version fields\r
2231      * of the logical palette structure.\r
2232      */\r
2233     pLogPal = (NPLOGPALETTE)\r
2234       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2235                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2236     pLogPal->palVersion    = 0x300;\r
2237   }\r
2238   pLogPal->palNumEntries = 0;\r
2239 \r
2240   InsertInPalette(lightSquareColor);\r
2241   InsertInPalette(darkSquareColor);\r
2242   InsertInPalette(whitePieceColor);\r
2243   InsertInPalette(blackPieceColor);\r
2244   InsertInPalette(highlightSquareColor);\r
2245   InsertInPalette(premoveHighlightColor);\r
2246 \r
2247   /*  create a logical color palette according the information\r
2248    *  in the LOGPALETTE structure.\r
2249    */\r
2250   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2251 \r
2252   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2253   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2254   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2255   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2256   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2257   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2258   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2259     for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers\r
2260 \r
2261    /* [AS] Force rendering of the font-based pieces */\r
2262   if( fontBitmapSquareSize > 0 ) {\r
2263     fontBitmapSquareSize = 0;\r
2264   }\r
2265 }\r
2266 \r
2267 \r
2268 int\r
2269 BoardWidth(int boardSize, int n)\r
2270 { /* [HGM] argument n added to allow different width and height */\r
2271   int lineGap = sizeInfo[boardSize].lineGap;\r
2272 \r
2273   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2274       lineGap = appData.overrideLineGap;\r
2275   }\r
2276 \r
2277   return (n + 1) * lineGap +\r
2278           n * sizeInfo[boardSize].squareSize;\r
2279 }\r
2280 \r
2281 /* Respond to board resize by dragging edge */\r
2282 VOID\r
2283 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2284 {\r
2285   BoardSize newSize = NUM_SIZES - 1;\r
2286   static int recurse = 0;\r
2287   if (IsIconic(hwndMain)) return;\r
2288   if (recurse > 0) return;\r
2289   recurse++;\r
2290   while (newSize > 0) {\r
2291         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2292         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2293            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2294     newSize--;\r
2295   } \r
2296   boardSize = newSize;\r
2297   InitDrawingSizes(boardSize, flags);\r
2298   recurse--;\r
2299 }\r
2300 \r
2301 \r
2302 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2303 \r
2304 VOID\r
2305 InitDrawingSizes(BoardSize boardSize, int flags)\r
2306 {\r
2307   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2308   ChessSquare piece;\r
2309   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2310   HDC hdc;\r
2311   SIZE clockSize, messageSize;\r
2312   HFONT oldFont;\r
2313   char buf[MSG_SIZ];\r
2314   char *str;\r
2315   HMENU hmenu = GetMenu(hwndMain);\r
2316   RECT crect, wrect, oldRect;\r
2317   int offby;\r
2318   LOGBRUSH logbrush;\r
2319   VariantClass v = gameInfo.variant;\r
2320 \r
2321   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2322   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2323 \r
2324   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2325   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2326   if(boardSize == -1) return;     // no size defined yet; abort (to allow early call of InitPosition)\r
2327   oldBoardSize = boardSize;\r
2328 \r
2329   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2330   { // correct board size to one where built-in pieces exist\r
2331     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2332        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2333 \r
2334       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2335       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2336       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy || v == VariantLion ) {\r
2337       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2338       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2339                                    boardSize = SizeMiddling;\r
2340     }\r
2341   }\r
2342   if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2343 \r
2344   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2345   oldRect.top = wpMain.y;\r
2346   oldRect.right = wpMain.x + wpMain.width;\r
2347   oldRect.bottom = wpMain.y + wpMain.height;\r
2348 \r
2349   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2350   smallLayout = sizeInfo[boardSize].smallLayout;\r
2351   squareSize = sizeInfo[boardSize].squareSize;\r
2352   lineGap = sizeInfo[boardSize].lineGap;\r
2353   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2354   border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;\r
2355 \r
2356   // [HGM] decide on tininess based on total board width rather than square size\r
2357   tinyLayout = squareSize * (BOARD_WIDTH);\r
2358   tinyLayout = tinyLayout < 35*8 ? 2 : tinyLayout < 43*8 ? 1 : 0;\r
2359 \r
2360   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2361       lineGap = appData.overrideLineGap;\r
2362   }\r
2363 \r
2364   if (tinyLayout != oldTinyLayout) {\r
2365     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2366     if (tinyLayout == 2) {\r
2367       style &= ~WS_SYSMENU;\r
2368       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2369                  "&Minimize\tCtrl+F4");\r
2370     } else {\r
2371       style |= WS_SYSMENU;\r
2372       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2373     }\r
2374     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2375 \r
2376     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2377       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2378         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2379     }\r
2380     DrawMenuBar(hwndMain);\r
2381   }\r
2382 \r
2383   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;\r
2384   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;\r
2385 \r
2386   /* Get text area sizes */\r
2387   hdc = GetDC(hwndMain);\r
2388   if (appData.clockMode) {\r
2389     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2390   } else {\r
2391     snprintf(buf, MSG_SIZ, _("White"));\r
2392   }\r
2393   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2394   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2395   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2396   str = _("We only care about the height here");\r
2397   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2398   SelectObject(hdc, oldFont);\r
2399   ReleaseDC(hwndMain, hdc);\r
2400 \r
2401   /* Compute where everything goes */\r
2402   if((first.programLogo || second.programLogo) && tinyLayout != 2) {\r
2403         /* [HGM] logo: if either logo is on, reserve space for it */\r
2404         logoHeight =  2*clockSize.cy;\r
2405         leftLogoRect.left   = OUTER_MARGIN;\r
2406         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2407         leftLogoRect.top    = OUTER_MARGIN;\r
2408         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2409 \r
2410         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2411         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2412         rightLogoRect.top    = OUTER_MARGIN;\r
2413         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2414 \r
2415 \r
2416     whiteRect.left = leftLogoRect.right;\r
2417     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2418     whiteRect.top = OUTER_MARGIN;\r
2419     whiteRect.bottom = whiteRect.top + logoHeight;\r
2420 \r
2421     blackRect.right = rightLogoRect.left;\r
2422     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2423     blackRect.top = whiteRect.top;\r
2424     blackRect.bottom = whiteRect.bottom;\r
2425   } else {\r
2426     whiteRect.left = OUTER_MARGIN;\r
2427     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2428     whiteRect.top = OUTER_MARGIN;\r
2429     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2430 \r
2431     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2432     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2433     blackRect.top = whiteRect.top;\r
2434     blackRect.bottom = whiteRect.bottom;\r
2435 \r
2436     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2437   }\r
2438 \r
2439   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2440   if (appData.showButtonBar) {\r
2441     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2442       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2443   } else {\r
2444     messageRect.right = OUTER_MARGIN + boardWidth;\r
2445   }\r
2446   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2447   messageRect.bottom = messageRect.top + messageSize.cy;\r
2448 \r
2449   boardRect.left = OUTER_MARGIN;\r
2450   boardRect.right = boardRect.left + boardWidth;\r
2451   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2452   boardRect.bottom = boardRect.top + boardHeight;\r
2453 \r
2454   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2455   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2456   oldTinyLayout = tinyLayout;\r
2457   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2458   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2459     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2460   winW *= 1 + twoBoards;\r
2461   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2462   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2463   wpMain.height = winH; //       without disturbing window attachments\r
2464   GetWindowRect(hwndMain, &wrect);\r
2465   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2466                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2467 \r
2468   // [HGM] placement: let attached windows follow size change.\r
2469   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2470   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2471   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2472   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2473   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2474 \r
2475   /* compensate if menu bar wrapped */\r
2476   GetClientRect(hwndMain, &crect);\r
2477   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2478   wpMain.height += offby;\r
2479   switch (flags) {\r
2480   case WMSZ_TOPLEFT:\r
2481     SetWindowPos(hwndMain, NULL, \r
2482                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2483                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2484     break;\r
2485 \r
2486   case WMSZ_TOPRIGHT:\r
2487   case WMSZ_TOP:\r
2488     SetWindowPos(hwndMain, NULL, \r
2489                  wrect.left, wrect.bottom - wpMain.height, \r
2490                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2491     break;\r
2492 \r
2493   case WMSZ_BOTTOMLEFT:\r
2494   case WMSZ_LEFT:\r
2495     SetWindowPos(hwndMain, NULL, \r
2496                  wrect.right - wpMain.width, wrect.top, \r
2497                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2498     break;\r
2499 \r
2500   case WMSZ_BOTTOMRIGHT:\r
2501   case WMSZ_BOTTOM:\r
2502   case WMSZ_RIGHT:\r
2503   default:\r
2504     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2505                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2506     break;\r
2507   }\r
2508 \r
2509   hwndPause = NULL;\r
2510   for (i = 0; i < N_BUTTONS; i++) {\r
2511     if (buttonDesc[i].hwnd != NULL) {\r
2512       DestroyWindow(buttonDesc[i].hwnd);\r
2513       buttonDesc[i].hwnd = NULL;\r
2514     }\r
2515     if (appData.showButtonBar) {\r
2516       buttonDesc[i].hwnd =\r
2517         CreateWindow("BUTTON", buttonDesc[i].label,\r
2518                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2519                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2520                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2521                      (HMENU) buttonDesc[i].id,\r
2522                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2523       if (tinyLayout == 2) {\r
2524         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2525                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2526                     MAKELPARAM(FALSE, 0));\r
2527       }\r
2528       if (buttonDesc[i].id == IDM_Pause)\r
2529         hwndPause = buttonDesc[i].hwnd;\r
2530       buttonDesc[i].wndproc = (WNDPROC)\r
2531         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2532     }\r
2533   }\r
2534   if (gridPen != NULL) DeleteObject(gridPen);\r
2535   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2536   if (premovePen != NULL) DeleteObject(premovePen);\r
2537   if (lineGap != 0) {\r
2538     logbrush.lbStyle = BS_SOLID;\r
2539     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2540     gridPen =\r
2541       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2542                    lineGap, &logbrush, 0, NULL);\r
2543     logbrush.lbColor = highlightSquareColor;\r
2544     highlightPen =\r
2545       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2546                    lineGap, &logbrush, 0, NULL);\r
2547 \r
2548     logbrush.lbColor = premoveHighlightColor; \r
2549     premovePen =\r
2550       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2551                    lineGap, &logbrush, 0, NULL);\r
2552 \r
2553     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2554     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2555       gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;\r
2556       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2557         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2558       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2559         BOARD_WIDTH * (squareSize + lineGap) + border;\r
2560       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2561     }\r
2562     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2563       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;\r
2564       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2565         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2566         lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2567       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2568         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;\r
2569       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2570     }\r
2571   }\r
2572 \r
2573   /* [HGM] Licensing requirement */\r
2574 #ifdef GOTHIC\r
2575   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2576 #endif\r
2577 #ifdef FALCON\r
2578   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2579 #endif\r
2580   GothicPopUp( "", VariantNormal);\r
2581 \r
2582 \r
2583 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2584 \r
2585   /* Load piece bitmaps for this board size */\r
2586   for (i=0; i<=2; i++) {\r
2587     for (piece = WhitePawn;\r
2588          (int) piece < (int) BlackPawn;\r
2589          piece = (ChessSquare) ((int) piece + 1)) {\r
2590       if (pieceBitmap[i][piece] != NULL)\r
2591         DeleteObject(pieceBitmap[i][piece]);\r
2592       pieceBitmap[i][piece] = NULL;\r
2593     }\r
2594   }\r
2595 \r
2596   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2597 \r
2598   // Orthodox Chess pieces\r
2599   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2600   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2601   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2602   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2603   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2604   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2605   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2606   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2607   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2608   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2609   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2610   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2611   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2612   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2613   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2614   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2615     // in Shogi, Hijack the unused Queen for Lance\r
2616     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2617     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2618     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2619   } else {\r
2620     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2621     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2622     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2623   }\r
2624 \r
2625   if(squareSize <= 72 && squareSize >= 33) { \r
2626     /* A & C are available in most sizes now */\r
2627     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2628       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2629       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2630       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2631       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2632       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2633       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2634       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2635       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2636       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2637       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2638       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2639       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2640     } else { // Smirf-like\r
2641       if(gameInfo.variant == VariantSChess) {\r
2642         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2643         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2644         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2645       } else {\r
2646         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2647         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2648         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2649       }\r
2650     }\r
2651     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2652       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2653       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2654       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2655     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2656       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2657       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2658       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2659     } else { // WinBoard standard\r
2660       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2661       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2662       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2663     }\r
2664   }\r
2665 \r
2666 \r
2667   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2668     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2669     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2670     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2671     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2672     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2673     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2674     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2675     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2676     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2677     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2678     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2679     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2680     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2681     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2682     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2683     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2684     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2685     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2686     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2687     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2688     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2689     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2690     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2691     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2692     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2693     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2694     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2695     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2696     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2697     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2698     pieceBitmap[0][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "s");\r
2699     pieceBitmap[1][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "o");\r
2700     pieceBitmap[2][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "w");\r
2701 \r
2702     if(gameInfo.variant == VariantShogi && BOARD_HEIGHT != 7) { /* promoted Gold representations (but not in Tori!)*/\r
2703       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2704       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2705       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2706       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2707       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2708       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2709       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2710       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2711       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2712       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2713       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2714       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2715     } else {\r
2716       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2717       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2718       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2719       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2720       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2721       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2722       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2723       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2724       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2725       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2726       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2727       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2728     }\r
2729 \r
2730   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2731     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2732     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2733     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2734     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2735     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2736     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2737     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2738     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2739     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2740     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2741     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2742     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2743     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2744     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2745   }\r
2746 \r
2747 \r
2748   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2749   /* special Shogi support in this size */\r
2750   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2751       for (piece = WhitePawn;\r
2752            (int) piece < (int) BlackPawn;\r
2753            piece = (ChessSquare) ((int) piece + 1)) {\r
2754         if (pieceBitmap[i][piece] != NULL)\r
2755           DeleteObject(pieceBitmap[i][piece]);\r
2756       }\r
2757     }\r
2758   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2759   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2760   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2761   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2762   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2763   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2764   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2765   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2766   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2767   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2768   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2769   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2770   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2771   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2772   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2773   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2774   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2775   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2776   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2777   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2778   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2779   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2780   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2781   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2782   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2783   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2784   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2785   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2786   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2787   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2788   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2789   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2790   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2791   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2792   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2793   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2794   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2795   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2796   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2797   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2798   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2799   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2800   minorSize = 0;\r
2801   }\r
2802 \r
2803   if(appData.pieceDirectory[0]) for(i=WhitePawn; i<BlackPawn; i++) { // try for all missing pieces with new naming convention\r
2804     char buf[MSG_SIZ];\r
2805     if(pieceBitmap[0][i]) continue;\r
2806     snprintf(buf, MSG_SIZ, "piece%d_", i);\r
2807     pieceBitmap[0][i] = DoLoadBitmap(hInst, buf, squareSize, "s");\r
2808     pieceBitmap[1][i] = DoLoadBitmap(hInst, buf, squareSize, "o");\r
2809     pieceBitmap[2][i] = DoLoadBitmap(hInst, buf, squareSize, "w");\r
2810   }\r
2811 }\r
2812 \r
2813 HBITMAP\r
2814 PieceBitmap(ChessSquare p, int kind)\r
2815 {\r
2816   if ((int) p >= (int) BlackPawn)\r
2817     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2818 \r
2819   return pieceBitmap[kind][(int) p];\r
2820 }\r
2821 \r
2822 /***************************************************************/\r
2823 \r
2824 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2825 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2826 /*\r
2827 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2828 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2829 */\r
2830 \r
2831 VOID\r
2832 SquareToPos(int row, int column, int * x, int * y)\r
2833 {\r
2834   if (flipView) {\r
2835     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
2836     *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;\r
2837   } else {\r
2838     *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;\r
2839     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
2840   }\r
2841 }\r
2842 \r
2843 VOID\r
2844 DrawCoordsOnDC(HDC hdc)\r
2845 {\r
2846   static char files[] = "0123456789012345678901221098765432109876543210";\r
2847   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2848   char str[2] = { NULLCHAR, NULLCHAR };\r
2849   int oldMode, oldAlign, x, y, start, i;\r
2850   HFONT oldFont;\r
2851   HBRUSH oldBrush;\r
2852 \r
2853   if (!appData.showCoords)\r
2854     return;\r
2855 \r
2856   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2857 \r
2858   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2859   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2860   oldAlign = GetTextAlign(hdc);\r
2861   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2862 \r
2863   y = boardRect.top + lineGap;\r
2864   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2865 \r
2866   if(border) {\r
2867     SetTextAlign(hdc, TA_RIGHT|TA_TOP);\r
2868     x += border - lineGap - 4; y += squareSize - 6;\r
2869   } else\r
2870   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2871   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2872     str[0] = files[start + i];\r
2873     ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);\r
2874     y += squareSize + lineGap;\r
2875   }\r
2876 \r
2877   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2878 \r
2879   if(border) {\r
2880     SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2881     x += -border + 4; y += border - squareSize + 6;\r
2882   } else\r
2883   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2884   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2885     str[0] = ranks[start + i];\r
2886     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2887     x += squareSize + lineGap;\r
2888   }    \r
2889 \r
2890   SelectObject(hdc, oldBrush);\r
2891   SetBkMode(hdc, oldMode);\r
2892   SetTextAlign(hdc, oldAlign);\r
2893   SelectObject(hdc, oldFont);\r
2894 }\r
2895 \r
2896 VOID\r
2897 DrawGridOnDC(HDC hdc)\r
2898 {\r
2899   HPEN oldPen;\r
2900  \r
2901   if (lineGap != 0) {\r
2902     oldPen = SelectObject(hdc, gridPen);\r
2903     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2904     SelectObject(hdc, oldPen);\r
2905   }\r
2906 }\r
2907 \r
2908 #define HIGHLIGHT_PEN 0\r
2909 #define PREMOVE_PEN   1\r
2910 \r
2911 VOID\r
2912 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2913 {\r
2914   int x1, y1;\r
2915   HPEN oldPen, hPen;\r
2916   if (lineGap == 0) return;\r
2917   if (flipView) {\r
2918     x1 = boardRect.left +\r
2919       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;\r
2920     y1 = boardRect.top +\r
2921       lineGap/2 + y * (squareSize + lineGap) + border;\r
2922   } else {\r
2923     x1 = boardRect.left +\r
2924       lineGap/2 + x * (squareSize + lineGap) + border;\r
2925     y1 = boardRect.top +\r
2926       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;\r
2927   }\r
2928   hPen = pen ? premovePen : highlightPen;\r
2929   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2930   MoveToEx(hdc, x1, y1, NULL);\r
2931   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2932   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2933   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2934   LineTo(hdc, x1, y1);\r
2935   SelectObject(hdc, oldPen);\r
2936 }\r
2937 \r
2938 VOID\r
2939 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2940 {\r
2941   int i;\r
2942   for (i=0; i<2; i++) {\r
2943     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2944       DrawHighlightOnDC(hdc, TRUE,\r
2945                         h->sq[i].x, h->sq[i].y,\r
2946                         pen);\r
2947   }\r
2948 }\r
2949 \r
2950 /* Note: sqcolor is used only in monoMode */\r
2951 /* Note that this code is largely duplicated in woptions.c,\r
2952    function DrawSampleSquare, so that needs to be updated too */\r
2953 VOID\r
2954 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2955 {\r
2956   HBITMAP oldBitmap;\r
2957   HBRUSH oldBrush;\r
2958   int tmpSize;\r
2959 \r
2960   if (appData.blindfold) return;\r
2961 \r
2962   /* [AS] Use font-based pieces if needed */\r
2963   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2964     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2965     CreatePiecesFromFont();\r
2966 \r
2967     if( fontBitmapSquareSize == squareSize ) {\r
2968         int index = TranslatePieceToFontPiece(piece);\r
2969 \r
2970         SelectObject( tmphdc, hPieceMask[ index ] );\r
2971 \r
2972       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2973         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2974       else\r
2975         BitBlt( hdc,\r
2976             x, y,\r
2977             squareSize, squareSize,\r
2978             tmphdc,\r
2979             0, 0,\r
2980             SRCAND );\r
2981 \r
2982         SelectObject( tmphdc, hPieceFace[ index ] );\r
2983 \r
2984       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2985         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2986       else\r
2987         BitBlt( hdc,\r
2988             x, y,\r
2989             squareSize, squareSize,\r
2990             tmphdc,\r
2991             0, 0,\r
2992             SRCPAINT );\r
2993 \r
2994         return;\r
2995     }\r
2996   }\r
2997 \r
2998   if (appData.monoMode) {\r
2999     SelectObject(tmphdc, PieceBitmap(piece, \r
3000       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3001     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3002            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3003   } else {\r
3004     HBRUSH xBrush = whitePieceBrush;\r
3005     tmpSize = squareSize;\r
3006     if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);\r
3007     if(minorSize &&\r
3008         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3009          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3010       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3011       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3012       x += (squareSize - minorSize)>>1;\r
3013       y += squareSize - minorSize - 2;\r
3014       tmpSize = minorSize;\r
3015     }\r
3016     if (color || appData.allWhite ) {\r
3017       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3018       if( color )\r
3019               oldBrush = SelectObject(hdc, xBrush);\r
3020       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3021       if(appData.upsideDown && color==flipView)\r
3022         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3023       else\r
3024         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3025       /* Use black for outline of white pieces */\r
3026       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3027       if(appData.upsideDown && color==flipView)\r
3028         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3029       else\r
3030         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3031     } else if(appData.pieceDirectory[0]) {\r
3032       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3033       oldBrush = SelectObject(hdc, xBrush);\r
3034       if(appData.upsideDown && color==flipView)\r
3035         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3036       else\r
3037         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3038       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3039       if(appData.upsideDown && color==flipView)\r
3040         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3041       else\r
3042         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3043     } else {\r
3044       /* Use square color for details of black pieces */\r
3045       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3046       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3047       if(appData.upsideDown && !flipView)\r
3048         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3049       else\r
3050         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3051     }\r
3052     SelectObject(hdc, oldBrush);\r
3053     SelectObject(tmphdc, oldBitmap);\r
3054   }\r
3055 }\r
3056 \r
3057 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3058 int GetBackTextureMode( int algo )\r
3059 {\r
3060     int result = BACK_TEXTURE_MODE_DISABLED;\r
3061 \r
3062     switch( algo ) \r
3063     {\r
3064         case BACK_TEXTURE_MODE_PLAIN:\r
3065             result = 1; /* Always use identity map */\r
3066             break;\r
3067         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3068             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3069             break;\r
3070     }\r
3071 \r
3072     return result;\r
3073 }\r
3074 \r
3075 /* \r
3076     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3077     to handle redraws cleanly (as random numbers would always be different).\r
3078 */\r
3079 VOID RebuildTextureSquareInfo()\r
3080 {\r
3081     BITMAP bi;\r
3082     int lite_w = 0;\r
3083     int lite_h = 0;\r
3084     int dark_w = 0;\r
3085     int dark_h = 0;\r
3086     int row;\r
3087     int col;\r
3088 \r
3089     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3090 \r
3091     if( liteBackTexture != NULL ) {\r
3092         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3093             lite_w = bi.bmWidth;\r
3094             lite_h = bi.bmHeight;\r
3095         }\r
3096     }\r
3097 \r
3098     if( darkBackTexture != NULL ) {\r
3099         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3100             dark_w = bi.bmWidth;\r
3101             dark_h = bi.bmHeight;\r
3102         }\r
3103     }\r
3104 \r
3105     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3106         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3107             if( (col + row) & 1 ) {\r
3108                 /* Lite square */\r
3109                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3110                   if( lite_w >= squareSize*BOARD_WIDTH )\r
3111                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
3112                   else\r
3113                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3114                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
3115                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
3116                   else\r
3117                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3118                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3119                 }\r
3120             }\r
3121             else {\r
3122                 /* Dark square */\r
3123                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3124                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3125                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3126                   else\r
3127                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3128                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3129                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3130                   else\r
3131                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3132                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3133                 }\r
3134             }\r
3135         }\r
3136     }\r
3137 }\r
3138 \r
3139 /* [AS] Arrow highlighting support */\r
3140 \r
3141 static double A_WIDTH = 5; /* Width of arrow body */\r
3142 \r
3143 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3144 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3145 \r
3146 static double Sqr( double x )\r
3147 {\r
3148     return x*x;\r
3149 }\r
3150 \r
3151 static int Round( double x )\r
3152 {\r
3153     return (int) (x + 0.5);\r
3154 }\r
3155 \r
3156 /* Draw an arrow between two points using current settings */\r
3157 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3158 {\r
3159     POINT arrow[7];\r
3160     double dx, dy, j, k, x, y;\r
3161 \r
3162     if( d_x == s_x ) {\r
3163         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3164 \r
3165         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3166         arrow[0].y = s_y;\r
3167 \r
3168         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3169         arrow[1].y = d_y - h;\r
3170 \r
3171         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3172         arrow[2].y = d_y - h;\r
3173 \r
3174         arrow[3].x = d_x;\r
3175         arrow[3].y = d_y;\r
3176 \r
3177         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3178         arrow[5].y = d_y - h;\r
3179 \r
3180         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3181         arrow[4].y = d_y - h;\r
3182 \r
3183         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3184         arrow[6].y = s_y;\r
3185     }\r
3186     else if( d_y == s_y ) {\r
3187         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3188 \r
3189         arrow[0].x = s_x;\r
3190         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3191 \r
3192         arrow[1].x = d_x - w;\r
3193         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3194 \r
3195         arrow[2].x = d_x - w;\r
3196         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3197 \r
3198         arrow[3].x = d_x;\r
3199         arrow[3].y = d_y;\r
3200 \r
3201         arrow[5].x = d_x - w;\r
3202         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3203 \r
3204         arrow[4].x = d_x - w;\r
3205         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3206 \r
3207         arrow[6].x = s_x;\r
3208         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3209     }\r
3210     else {\r
3211         /* [AS] Needed a lot of paper for this! :-) */\r
3212         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3213         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3214   \r
3215         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3216 \r
3217         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3218 \r
3219         x = s_x;\r
3220         y = s_y;\r
3221 \r
3222         arrow[0].x = Round(x - j);\r
3223         arrow[0].y = Round(y + j*dx);\r
3224 \r
3225         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3226         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3227 \r
3228         if( d_x > s_x ) {\r
3229             x = (double) d_x - k;\r
3230             y = (double) d_y - k*dy;\r
3231         }\r
3232         else {\r
3233             x = (double) d_x + k;\r
3234             y = (double) d_y + k*dy;\r
3235         }\r
3236 \r
3237         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3238 \r
3239         arrow[6].x = Round(x - j);\r
3240         arrow[6].y = Round(y + j*dx);\r
3241 \r
3242         arrow[2].x = Round(arrow[6].x + 2*j);\r
3243         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3244 \r
3245         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3246         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3247 \r
3248         arrow[4].x = d_x;\r
3249         arrow[4].y = d_y;\r
3250 \r
3251         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3252         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3253     }\r
3254 \r
3255     Polygon( hdc, arrow, 7 );\r
3256 }\r
3257 \r
3258 /* [AS] Draw an arrow between two squares */\r
3259 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3260 {\r
3261     int s_x, s_y, d_x, d_y;\r
3262     HPEN hpen;\r
3263     HPEN holdpen;\r
3264     HBRUSH hbrush;\r
3265     HBRUSH holdbrush;\r
3266     LOGBRUSH stLB;\r
3267 \r
3268     if( s_col == d_col && s_row == d_row ) {\r
3269         return;\r
3270     }\r
3271 \r
3272     /* Get source and destination points */\r
3273     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3274     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3275 \r
3276     if( d_y > s_y ) {\r
3277         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3278     }\r
3279     else if( d_y < s_y ) {\r
3280         d_y += squareSize / 2 + squareSize / 4;\r
3281     }\r
3282     else {\r
3283         d_y += squareSize / 2;\r
3284     }\r
3285 \r
3286     if( d_x > s_x ) {\r
3287         d_x += squareSize / 2 - squareSize / 4;\r
3288     }\r
3289     else if( d_x < s_x ) {\r
3290         d_x += squareSize / 2 + squareSize / 4;\r
3291     }\r
3292     else {\r
3293         d_x += squareSize / 2;\r
3294     }\r
3295 \r
3296     s_x += squareSize / 2;\r
3297     s_y += squareSize / 2;\r
3298 \r
3299     /* Adjust width */\r
3300     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3301 \r
3302     /* Draw */\r
3303     stLB.lbStyle = BS_SOLID;\r
3304     stLB.lbColor = appData.highlightArrowColor;\r
3305     stLB.lbHatch = 0;\r
3306 \r
3307     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3308     holdpen = SelectObject( hdc, hpen );\r
3309     hbrush = CreateBrushIndirect( &stLB );\r
3310     holdbrush = SelectObject( hdc, hbrush );\r
3311 \r
3312     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3313 \r
3314     SelectObject( hdc, holdpen );\r
3315     SelectObject( hdc, holdbrush );\r
3316     DeleteObject( hpen );\r
3317     DeleteObject( hbrush );\r
3318 }\r
3319 \r
3320 BOOL HasHighlightInfo()\r
3321 {\r
3322     BOOL result = FALSE;\r
3323 \r
3324     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3325         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3326     {\r
3327         result = TRUE;\r
3328     }\r
3329 \r
3330     return result;\r
3331 \r
3332 \r
3333 \r
3334 }\r
3335 \r
3336 BOOL IsDrawArrowEnabled()\r
3337 {\r
3338     BOOL result = FALSE;\r
3339 \r
3340     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3341         result = TRUE;\r
3342     }\r
3343 \r
3344     return result;\r
3345 }\r
3346 \r
3347 VOID DrawArrowHighlight( HDC hdc )\r
3348 {\r
3349     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3350         DrawArrowBetweenSquares( hdc,\r
3351             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3352             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3353     }\r
3354 }\r
3355 \r
3356 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3357 {\r
3358     HRGN result = NULL;\r
3359 \r
3360     if( HasHighlightInfo() ) {\r
3361         int x1, y1, x2, y2;\r
3362         int sx, sy, dx, dy;\r
3363 \r
3364         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3365         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3366 \r
3367         sx = MIN( x1, x2 );\r
3368         sy = MIN( y1, y2 );\r
3369         dx = MAX( x1, x2 ) + squareSize;\r
3370         dy = MAX( y1, y2 ) + squareSize;\r
3371 \r
3372         result = CreateRectRgn( sx, sy, dx, dy );\r
3373     }\r
3374 \r
3375     return result;\r
3376 }\r
3377 \r
3378 /*\r
3379     Warning: this function modifies the behavior of several other functions. \r
3380     \r
3381     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3382     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3383     repaint is scattered all over the place, which is not good for features such as\r
3384     "arrow highlighting" that require a full repaint of the board.\r
3385 \r
3386     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3387     user interaction, when speed is not so important) but especially to avoid errors\r
3388     in the displayed graphics.\r
3389 \r
3390     In such patched places, I always try refer to this function so there is a single\r
3391     place to maintain knowledge.\r
3392     \r
3393     To restore the original behavior, just return FALSE unconditionally.\r
3394 */\r
3395 BOOL IsFullRepaintPreferrable()\r
3396 {\r
3397     BOOL result = FALSE;\r
3398 \r
3399     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3400         /* Arrow may appear on the board */\r
3401         result = TRUE;\r
3402     }\r
3403 \r
3404     return result;\r
3405 }\r
3406 \r
3407 /* \r
3408     This function is called by DrawPosition to know whether a full repaint must\r
3409     be forced or not.\r
3410 \r
3411     Only DrawPosition may directly call this function, which makes use of \r
3412     some state information. Other function should call DrawPosition specifying \r
3413     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3414 */\r
3415 BOOL DrawPositionNeedsFullRepaint()\r
3416 {\r
3417     BOOL result = FALSE;\r
3418 \r
3419     /* \r
3420         Probably a slightly better policy would be to trigger a full repaint\r
3421         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3422         but animation is fast enough that it's difficult to notice.\r
3423     */\r
3424     if( animInfo.piece == EmptySquare ) {\r
3425         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3426             result = TRUE;\r
3427         }\r
3428     }\r
3429 \r
3430     return result;\r
3431 }\r
3432 \r
3433 static HBITMAP borderBitmap;\r
3434 \r
3435 VOID\r
3436 DrawBackgroundOnDC(HDC hdc)\r
3437 {\r
3438   \r
3439   BITMAP bi;\r
3440   HDC tmphdc;\r
3441   HBITMAP hbm;\r
3442   static char oldBorder[MSG_SIZ];\r
3443   int w = 600, h = 600, mode;\r
3444 \r
3445   if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid\r
3446     strncpy(oldBorder, appData.border, MSG_SIZ-1);\r
3447     borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
3448   }\r
3449   if(borderBitmap == NULL) { // loading failed, use white\r
3450     FillRect( hdc, &boardRect, whitePieceBrush );\r
3451     return;\r
3452   }\r
3453   tmphdc = CreateCompatibleDC(hdc);\r
3454   hbm = SelectObject(tmphdc, borderBitmap);\r
3455   if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {\r
3456             w = bi.bmWidth;\r
3457             h = bi.bmHeight;\r
3458   }\r
3459   mode = SetStretchBltMode(hdc, COLORONCOLOR);\r
3460   StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left, \r
3461                   boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3462   SetStretchBltMode(hdc, mode);\r
3463   SelectObject(tmphdc, hbm);\r
3464   DeleteDC(tmphdc);\r
3465 }\r
3466 \r
3467 VOID\r
3468 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3469 {\r
3470   int row, column, x, y, square_color, piece_color;\r
3471   ChessSquare piece;\r
3472   HBRUSH oldBrush;\r
3473   HDC texture_hdc = NULL;\r
3474 \r
3475   /* [AS] Initialize background textures if needed */\r
3476   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3477       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3478       if( backTextureSquareSize != squareSize \r
3479        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3480           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3481           backTextureSquareSize = squareSize;\r
3482           RebuildTextureSquareInfo();\r
3483       }\r
3484 \r
3485       texture_hdc = CreateCompatibleDC( hdc );\r
3486   }\r
3487 \r
3488   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3489     for (column = 0; column < BOARD_WIDTH; column++) {\r
3490   \r
3491       SquareToPos(row, column, &x, &y);\r
3492 \r
3493       piece = board[row][column];\r
3494 \r
3495       square_color = ((column + row) % 2) == 1;\r
3496       if( gameInfo.variant == VariantXiangqi ) {\r
3497           square_color = !InPalace(row, column);\r
3498           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3499           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3500       }\r
3501       piece_color = (int) piece < (int) BlackPawn;\r
3502 \r
3503 \r
3504       /* [HGM] holdings file: light square or black */\r
3505       if(column == BOARD_LEFT-2) {\r
3506             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3507                 square_color = 1;\r
3508             else {\r
3509                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3510                 continue;\r
3511             }\r
3512       } else\r
3513       if(column == BOARD_RGHT + 1 ) {\r
3514             if( row < gameInfo.holdingsSize )\r
3515                 square_color = 1;\r
3516             else {\r
3517                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3518                 continue;\r
3519             }\r
3520       }\r
3521       if(column == BOARD_LEFT-1 ) /* left align */\r
3522             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3523       else if( column == BOARD_RGHT) /* right align */\r
3524             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3525       else if( piece == DarkSquare) DisplayHoldingsCount(hdc, x, y, 0, 0);\r
3526       else\r
3527       if (appData.monoMode) {\r
3528         if (piece == EmptySquare) {\r
3529           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3530                  square_color ? WHITENESS : BLACKNESS);\r
3531         } else {\r
3532           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3533         }\r
3534       } \r
3535       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3536           /* [AS] Draw the square using a texture bitmap */\r
3537           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3538           int r = row, c = column; // [HGM] do not flip board in flipView\r
3539           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3540 \r
3541           DrawTile( x, y, \r
3542               squareSize, squareSize, \r
3543               hdc, \r
3544               texture_hdc,\r
3545               backTextureSquareInfo[r][c].mode,\r
3546               backTextureSquareInfo[r][c].x,\r
3547               backTextureSquareInfo[r][c].y );\r
3548 \r
3549           SelectObject( texture_hdc, hbm );\r
3550 \r
3551           if (piece != EmptySquare) {\r
3552               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3553           }\r
3554       }\r
3555       else {\r
3556         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3557 \r
3558         oldBrush = SelectObject(hdc, brush );\r
3559         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3560         SelectObject(hdc, oldBrush);\r
3561         if (piece != EmptySquare)\r
3562           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3563       }\r
3564     }\r
3565   }\r
3566 \r
3567   if( texture_hdc != NULL ) {\r
3568     DeleteDC( texture_hdc );\r
3569   }\r
3570 }\r
3571 \r
3572 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3573 void fputDW(FILE *f, int x)\r
3574 {\r
3575         fputc(x     & 255, f);\r
3576         fputc(x>>8  & 255, f);\r
3577         fputc(x>>16 & 255, f);\r
3578         fputc(x>>24 & 255, f);\r
3579 }\r
3580 \r
3581 #define MAX_CLIPS 200   /* more than enough */\r
3582 \r
3583 VOID\r
3584 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3585 {\r
3586 //  HBITMAP bufferBitmap;\r
3587   BITMAP bi;\r
3588 //  RECT Rect;\r
3589   HDC tmphdc;\r
3590   HBITMAP hbm;\r
3591   int w = 100, h = 50;\r
3592 \r
3593   if(logo == NULL) {\r
3594     if(!logoHeight) return;\r
3595     FillRect( hdc, &logoRect, whitePieceBrush );\r
3596   }\r
3597 //  GetClientRect(hwndMain, &Rect);\r
3598 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3599 //                                      Rect.bottom-Rect.top+1);\r
3600   tmphdc = CreateCompatibleDC(hdc);\r
3601   hbm = SelectObject(tmphdc, logo);\r
3602   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3603             w = bi.bmWidth;\r
3604             h = bi.bmHeight;\r
3605   }\r
3606   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3607                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3608   SelectObject(tmphdc, hbm);\r
3609   DeleteDC(tmphdc);\r
3610 }\r
3611 \r
3612 VOID\r
3613 DisplayLogos()\r
3614 {\r
3615   if(logoHeight) {\r
3616         HDC hdc = GetDC(hwndMain);\r
3617         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3618         if(appData.autoLogo) {\r
3619           \r
3620           switch(gameMode) { // pick logos based on game mode\r
3621             case IcsObserving:\r
3622                 whiteLogo = second.programLogo; // ICS logo\r
3623                 blackLogo = second.programLogo;\r
3624             default:\r
3625                 break;\r
3626             case IcsPlayingWhite:\r
3627                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3628                 blackLogo = second.programLogo; // ICS logo\r
3629                 break;\r
3630             case IcsPlayingBlack:\r
3631                 whiteLogo = second.programLogo; // ICS logo\r
3632                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3633                 break;\r
3634             case TwoMachinesPlay:\r
3635                 if(first.twoMachinesColor[0] == 'b') {\r
3636                     whiteLogo = second.programLogo;\r
3637                     blackLogo = first.programLogo;\r
3638                 }\r
3639                 break;\r
3640             case MachinePlaysWhite:\r
3641                 blackLogo = userLogo;\r
3642                 break;\r
3643             case MachinePlaysBlack:\r
3644                 whiteLogo = userLogo;\r
3645                 blackLogo = first.programLogo;\r
3646           }\r
3647         }\r
3648         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3649         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3650         ReleaseDC(hwndMain, hdc);\r
3651   }\r
3652 }\r
3653 \r
3654 void\r
3655 UpdateLogos(int display)\r
3656 { // called after loading new engine(s), in tourney or from menu\r
3657   LoadLogo(&first, 0, FALSE);\r
3658   LoadLogo(&second, 1, appData.icsActive);\r
3659   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3660   if(display) DisplayLogos();\r
3661 }\r
3662 \r
3663 static HDC hdcSeek;\r
3664 \r
3665 // [HGM] seekgraph\r
3666 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3667 {\r
3668     POINT stPt;\r
3669     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3670     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3671     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3672     SelectObject( hdcSeek, hp );\r
3673 }\r
3674 \r
3675 // front-end wrapper for drawing functions to do rectangles\r
3676 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3677 {\r
3678     HPEN hp;\r
3679     RECT rc;\r
3680 \r
3681     if (hdcSeek == NULL) {\r
3682     hdcSeek = GetDC(hwndMain);\r
3683       if (!appData.monoMode) {\r
3684         SelectPalette(hdcSeek, hPal, FALSE);\r
3685         RealizePalette(hdcSeek);\r
3686       }\r
3687     }\r
3688     hp = SelectObject( hdcSeek, gridPen );\r
3689     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3690     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3691     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3692     SelectObject( hdcSeek, hp );\r
3693 }\r
3694 \r
3695 // front-end wrapper for putting text in graph\r
3696 void DrawSeekText(char *buf, int x, int y)\r
3697 {\r
3698         SIZE stSize;\r
3699         SetBkMode( hdcSeek, TRANSPARENT );\r
3700         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3701         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3702 }\r
3703 \r
3704 void DrawSeekDot(int x, int y, int color)\r
3705 {\r
3706         int square = color & 0x80;\r
3707         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3708                         color == 0 ? markerBrush[1] : color == 1 ? darkSquareBrush : explodeBrush);\r
3709         color &= 0x7F;\r
3710         if(square)\r
3711             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3712                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3713         else\r
3714             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3715                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3716             SelectObject(hdcSeek, oldBrush);\r
3717 }\r
3718 \r
3719 void DrawSeekOpen()\r
3720 {\r
3721 }\r
3722 \r
3723 void DrawSeekClose()\r
3724 {\r
3725 }\r
3726 \r
3727 VOID\r
3728 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3729 {\r
3730   static Board lastReq[2], lastDrawn[2];\r
3731   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3732   static int lastDrawnFlipView = 0;\r
3733   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3734   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3735   HDC tmphdc;\r
3736   HDC hdcmem;\r
3737   HBITMAP bufferBitmap;\r
3738   HBITMAP oldBitmap;\r
3739   RECT Rect;\r
3740   HRGN clips[MAX_CLIPS];\r
3741   ChessSquare dragged_piece = EmptySquare;\r
3742   int nr = twoBoards*partnerUp;\r
3743 \r
3744   /* I'm undecided on this - this function figures out whether a full\r
3745    * repaint is necessary on its own, so there's no real reason to have the\r
3746    * caller tell it that.  I think this can safely be set to FALSE - but\r
3747    * if we trust the callers not to request full repaints unnessesarily, then\r
3748    * we could skip some clipping work.  In other words, only request a full\r
3749    * redraw when the majority of pieces have changed positions (ie. flip, \r
3750    * gamestart and similar)  --Hawk\r
3751    */\r
3752   Boolean fullrepaint = repaint;\r
3753 \r
3754   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3755 \r
3756   if( DrawPositionNeedsFullRepaint() ) {\r
3757       fullrepaint = TRUE;\r
3758   }\r
3759 \r
3760   if (board == NULL) {\r
3761     if (!lastReqValid[nr]) {\r
3762       return;\r
3763     }\r
3764     board = lastReq[nr];\r
3765   } else {\r
3766     CopyBoard(lastReq[nr], board);\r
3767     lastReqValid[nr] = 1;\r
3768   }\r
3769 \r
3770   if (doingSizing) {\r
3771     return;\r
3772   }\r
3773 \r
3774   if (IsIconic(hwndMain)) {\r
3775     return;\r
3776   }\r
3777 \r
3778   if (hdc == NULL) {\r
3779     hdc = GetDC(hwndMain);\r
3780     if (!appData.monoMode) {\r
3781       SelectPalette(hdc, hPal, FALSE);\r
3782       RealizePalette(hdc);\r
3783     }\r
3784     releaseDC = TRUE;\r
3785   } else {\r
3786     releaseDC = FALSE;\r
3787   }\r
3788 \r
3789   /* Create some work-DCs */\r
3790   hdcmem = CreateCompatibleDC(hdc);\r
3791   tmphdc = CreateCompatibleDC(hdc);\r
3792 \r
3793   /* If dragging is in progress, we temporarely remove the piece */\r
3794   /* [HGM] or temporarily decrease count if stacked              */\r
3795   /*       !! Moved to before board compare !!                   */\r
3796   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3797     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3798     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3799             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3800         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3801     } else \r
3802     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3803             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3804         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3805     } else \r
3806         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3807   }\r
3808 \r
3809   /* Figure out which squares need updating by comparing the \r
3810    * newest board with the last drawn board and checking if\r
3811    * flipping has changed.\r
3812    */\r
3813   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3814     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3815       for (column = 0; column < BOARD_WIDTH; column++) {\r
3816         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3817           SquareToPos(row, column, &x, &y);\r
3818           clips[num_clips++] =\r
3819             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3820         }\r
3821       }\r
3822     }\r
3823    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3824     for (i=0; i<2; i++) {\r
3825       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3826           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3827         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3828             lastDrawnHighlight.sq[i].y >= 0) {\r
3829           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3830                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3831           clips[num_clips++] =\r
3832             CreateRectRgn(x - lineGap, y - lineGap, \r
3833                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3834         }\r
3835         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3836           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3837           clips[num_clips++] =\r
3838             CreateRectRgn(x - lineGap, y - lineGap, \r
3839                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3840         }\r
3841       }\r
3842     }\r
3843     for (i=0; i<2; i++) {\r
3844       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3845           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3846         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3847             lastDrawnPremove.sq[i].y >= 0) {\r
3848           SquareToPos(lastDrawnPremove.sq[i].y,\r
3849                       lastDrawnPremove.sq[i].x, &x, &y);\r
3850           clips[num_clips++] =\r
3851             CreateRectRgn(x - lineGap, y - lineGap, \r
3852                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3853         }\r
3854         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3855             premoveHighlightInfo.sq[i].y >= 0) {\r
3856           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3857                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3858           clips[num_clips++] =\r
3859             CreateRectRgn(x - lineGap, y - lineGap, \r
3860                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3861         }\r
3862       }\r
3863     }\r
3864    } else { // nr == 1\r
3865         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3866         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3867         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3868         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3869       for (i=0; i<2; i++) {\r
3870         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3871             partnerHighlightInfo.sq[i].y >= 0) {\r
3872           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3873                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3874           clips[num_clips++] =\r
3875             CreateRectRgn(x - lineGap, y - lineGap, \r
3876                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3877         }\r
3878         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3879             oldPartnerHighlight.sq[i].y >= 0) {\r
3880           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3881                       oldPartnerHighlight.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       }\r
3887    }\r
3888   } else {\r
3889     fullrepaint = TRUE;\r
3890   }\r
3891 \r
3892   /* Create a buffer bitmap - this is the actual bitmap\r
3893    * being written to.  When all the work is done, we can\r
3894    * copy it to the real DC (the screen).  This avoids\r
3895    * the problems with flickering.\r
3896    */\r
3897   GetClientRect(hwndMain, &Rect);\r
3898   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3899                                         Rect.bottom-Rect.top+1);\r
3900   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3901   if (!appData.monoMode) {\r
3902     SelectPalette(hdcmem, hPal, FALSE);\r
3903   }\r
3904 \r
3905   /* Create clips for dragging */\r
3906   if (!fullrepaint) {\r
3907     if (dragInfo.from.x >= 0) {\r
3908       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3909       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3910     }\r
3911     if (dragInfo.start.x >= 0) {\r
3912       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3913       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3914     }\r
3915     if (dragInfo.pos.x >= 0) {\r
3916       x = dragInfo.pos.x - squareSize / 2;\r
3917       y = dragInfo.pos.y - squareSize / 2;\r
3918       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3919     }\r
3920     if (dragInfo.lastpos.x >= 0) {\r
3921       x = dragInfo.lastpos.x - squareSize / 2;\r
3922       y = dragInfo.lastpos.y - squareSize / 2;\r
3923       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3924     }\r
3925   }\r
3926 \r
3927   /* Are we animating a move?  \r
3928    * If so, \r
3929    *   - remove the piece from the board (temporarely)\r
3930    *   - calculate the clipping region\r
3931    */\r
3932   if (!fullrepaint) {\r
3933     if (animInfo.piece != EmptySquare) {\r
3934       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3935       x = boardRect.left + animInfo.lastpos.x;\r
3936       y = boardRect.top + animInfo.lastpos.y;\r
3937       x2 = boardRect.left + animInfo.pos.x;\r
3938       y2 = boardRect.top + animInfo.pos.y;\r
3939       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3940       /* Slight kludge.  The real problem is that after AnimateMove is\r
3941          done, the position on the screen does not match lastDrawn.\r
3942          This currently causes trouble only on e.p. captures in\r
3943          atomic, where the piece moves to an empty square and then\r
3944          explodes.  The old and new positions both had an empty square\r
3945          at the destination, but animation has drawn a piece there and\r
3946          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3947       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3948     }\r
3949   }\r
3950 \r
3951   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3952   if (num_clips == 0)\r
3953     fullrepaint = TRUE;\r
3954 \r
3955   /* Set clipping on the memory DC */\r
3956   if (!fullrepaint) {\r
3957     SelectClipRgn(hdcmem, clips[0]);\r
3958     for (x = 1; x < num_clips; x++) {\r
3959       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3960         abort();  // this should never ever happen!\r
3961     }\r
3962   }\r
3963 \r
3964   /* Do all the drawing to the memory DC */\r
3965   if(explodeInfo.radius) { // [HGM] atomic\r
3966         HBRUSH oldBrush;\r
3967         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3968         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3969         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3970         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3971         x += squareSize/2;\r
3972         y += squareSize/2;\r
3973         if(!fullrepaint) {\r
3974           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3975           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3976         }\r
3977         DrawGridOnDC(hdcmem);\r
3978         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3979         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3980         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3981         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3982         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3983         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3984         SelectObject(hdcmem, oldBrush);\r
3985   } else {\r
3986     if(border) DrawBackgroundOnDC(hdcmem);\r
3987     DrawGridOnDC(hdcmem);\r
3988     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3989         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3990         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3991     } else {\r
3992         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3993         oldPartnerHighlight = partnerHighlightInfo;\r
3994     }\r
3995     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3996   }\r
3997   if(nr == 0) // [HGM] dual: markers only on left board\r
3998   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3999     for (column = 0; column < BOARD_WIDTH; column++) {\r
4000         if (marker[row][column]) { // marker changes only occur with full repaint!\r
4001             HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);\r
4002             SquareToPos(row, column, &x, &y);\r
4003             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
4004                           x + 3*squareSize/4, y + 3*squareSize/4);\r
4005             SelectObject(hdcmem, oldBrush);\r
4006         }\r
4007     }\r
4008   }\r
4009 \r
4010   if( appData.highlightMoveWithArrow ) {\r
4011 \r
4012     DrawArrowHighlight(hdcmem);\r
4013   }\r
4014 \r
4015   DrawCoordsOnDC(hdcmem);\r
4016 \r
4017   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
4018                  /* to make sure lastDrawn contains what is actually drawn */\r
4019 \r
4020   /* Put the dragged piece back into place and draw it (out of place!) */\r
4021     if (dragged_piece != EmptySquare) {\r
4022     /* [HGM] or restack */\r
4023     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4024                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4025     else\r
4026     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4027                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4028 \r
4029     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4030     x = dragInfo.pos.x - squareSize / 2;\r
4031     y = dragInfo.pos.y - squareSize / 2;\r
4032     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
4033                   ((int) dragInfo.piece < (int) BlackPawn), \r
4034                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4035   }   \r
4036   \r
4037   /* Put the animated piece back into place and draw it */\r
4038   if (animInfo.piece != EmptySquare) {\r
4039     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4040     x = boardRect.left + animInfo.pos.x;\r
4041     y = boardRect.top + animInfo.pos.y;\r
4042     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4043                   ((int) animInfo.piece < (int) BlackPawn),\r
4044                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4045   }\r
4046 \r
4047   /* Release the bufferBitmap by selecting in the old bitmap \r
4048    * and delete the memory DC\r
4049    */\r
4050   SelectObject(hdcmem, oldBitmap);\r
4051   DeleteDC(hdcmem);\r
4052 \r
4053   /* Set clipping on the target DC */\r
4054   if (!fullrepaint) {\r
4055     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
4056         RECT rect;\r
4057         GetRgnBox(clips[x], &rect);\r
4058         DeleteObject(clips[x]);\r
4059         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
4060                           rect.right + wpMain.width/2, rect.bottom);\r
4061     }\r
4062     SelectClipRgn(hdc, clips[0]);\r
4063     for (x = 1; x < num_clips; x++) {\r
4064       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4065         abort();   // this should never ever happen!\r
4066     } \r
4067   }\r
4068 \r
4069   /* Copy the new bitmap onto the screen in one go.\r
4070    * This way we avoid any flickering\r
4071    */\r
4072   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4073   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
4074          boardRect.right - boardRect.left,\r
4075          boardRect.bottom - boardRect.top,\r
4076          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4077   if(saveDiagFlag) { \r
4078     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
4079     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4080 \r
4081     GetObject(bufferBitmap, sizeof(b), &b);\r
4082     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
4083         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4084         bih.biWidth = b.bmWidth;\r
4085         bih.biHeight = b.bmHeight;\r
4086         bih.biPlanes = 1;\r
4087         bih.biBitCount = b.bmBitsPixel;\r
4088         bih.biCompression = 0;\r
4089         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4090         bih.biXPelsPerMeter = 0;\r
4091         bih.biYPelsPerMeter = 0;\r
4092         bih.biClrUsed = 0;\r
4093         bih.biClrImportant = 0;\r
4094 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4095 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4096         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4097 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4098 \r
4099         wb = b.bmWidthBytes;\r
4100         // count colors\r
4101         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4102                 int k = ((int*) pData)[i];\r
4103                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4104                 if(j >= 16) break;\r
4105                 color[j] = k;\r
4106                 if(j >= nrColors) nrColors = j+1;\r
4107         }\r
4108         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4109                 INT p = 0;\r
4110                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4111                     for(w=0; w<(wb>>2); w+=2) {\r
4112                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4113                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4114                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4115                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4116                         pData[p++] = m | j<<4;\r
4117                     }\r
4118                     while(p&3) pData[p++] = 0;\r
4119                 }\r
4120                 fac = 3;\r
4121                 wb = ((wb+31)>>5)<<2;\r
4122         }\r
4123         // write BITMAPFILEHEADER\r
4124         fprintf(diagFile, "BM");\r
4125         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4126         fputDW(diagFile, 0);\r
4127         fputDW(diagFile, 0x36 + (fac?64:0));\r
4128         // write BITMAPINFOHEADER\r
4129         fputDW(diagFile, 40);\r
4130         fputDW(diagFile, b.bmWidth);\r
4131         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4132         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4133         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4134         fputDW(diagFile, 0);\r
4135         fputDW(diagFile, 0);\r
4136         fputDW(diagFile, 0);\r
4137         fputDW(diagFile, 0);\r
4138         fputDW(diagFile, 0);\r
4139         fputDW(diagFile, 0);\r
4140         // write color table\r
4141         if(fac)\r
4142         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4143         // write bitmap data\r
4144         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4145                 fputc(pData[i], diagFile);\r
4146         free(pData);\r
4147      }\r
4148   }\r
4149 \r
4150   SelectObject(tmphdc, oldBitmap);\r
4151 \r
4152   /* Massive cleanup */\r
4153   for (x = 0; x < num_clips; x++)\r
4154     DeleteObject(clips[x]);\r
4155 \r
4156   DeleteDC(tmphdc);\r
4157   DeleteObject(bufferBitmap);\r
4158 \r
4159   if (releaseDC) \r
4160     ReleaseDC(hwndMain, hdc);\r
4161   \r
4162   if (lastDrawnFlipView != flipView && nr == 0) {\r
4163     if (flipView)\r
4164       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4165     else\r
4166       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4167   }\r
4168 \r
4169 /*  CopyBoard(lastDrawn, board);*/\r
4170   lastDrawnHighlight = highlightInfo;\r
4171   lastDrawnPremove   = premoveHighlightInfo;\r
4172   lastDrawnFlipView = flipView;\r
4173   lastDrawnValid[nr] = 1;\r
4174 }\r
4175 \r
4176 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4177 int\r
4178 SaveDiagram(f)\r
4179      FILE *f;\r
4180 {\r
4181     saveDiagFlag = 1; diagFile = f;\r
4182     HDCDrawPosition(NULL, TRUE, NULL);\r
4183     saveDiagFlag = 0;\r
4184 \r
4185     fclose(f);\r
4186     return TRUE;\r
4187 }\r
4188 \r
4189 \r
4190 /*---------------------------------------------------------------------------*\\r
4191 | CLIENT PAINT PROCEDURE\r
4192 |   This is the main event-handler for the WM_PAINT message.\r
4193 |\r
4194 \*---------------------------------------------------------------------------*/\r
4195 VOID\r
4196 PaintProc(HWND hwnd)\r
4197 {\r
4198   HDC         hdc;\r
4199   PAINTSTRUCT ps;\r
4200   HFONT       oldFont;\r
4201 \r
4202   if((hdc = BeginPaint(hwnd, &ps))) {\r
4203     if (IsIconic(hwnd)) {\r
4204       DrawIcon(hdc, 2, 2, iconCurrent);\r
4205     } else {\r
4206       if (!appData.monoMode) {\r
4207         SelectPalette(hdc, hPal, FALSE);\r
4208         RealizePalette(hdc);\r
4209       }\r
4210       HDCDrawPosition(hdc, 1, NULL);\r
4211       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4212         flipView = !flipView; partnerUp = !partnerUp;\r
4213         HDCDrawPosition(hdc, 1, NULL);\r
4214         flipView = !flipView; partnerUp = !partnerUp;\r
4215       }\r
4216       oldFont =\r
4217         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4218       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4219                  ETO_CLIPPED|ETO_OPAQUE,\r
4220                  &messageRect, messageText, strlen(messageText), NULL);\r
4221       SelectObject(hdc, oldFont);\r
4222       DisplayBothClocks();\r
4223       DisplayLogos();\r
4224     }\r
4225     EndPaint(hwnd,&ps);\r
4226   }\r
4227 \r
4228   return;\r
4229 }\r
4230 \r
4231 \r
4232 /*\r
4233  * If the user selects on a border boundary, return -1; if off the board,\r
4234  *   return -2.  Otherwise map the event coordinate to the square.\r
4235  * The offset boardRect.left or boardRect.top must already have been\r
4236  *   subtracted from x.\r
4237  */\r
4238 int EventToSquare(x, limit)\r
4239      int x, limit;\r
4240 {\r
4241   if (x <= border)\r
4242     return -2;\r
4243   if (x < lineGap + border)\r
4244     return -1;\r
4245   x -= lineGap + border;\r
4246   if ((x % (squareSize + lineGap)) >= squareSize)\r
4247     return -1;\r
4248   x /= (squareSize + lineGap);\r
4249     if (x >= limit)\r
4250     return -2;\r
4251   return x;\r
4252 }\r
4253 \r
4254 typedef struct {\r
4255   char piece;\r
4256   int command;\r
4257   char* name;\r
4258 } DropEnable;\r
4259 \r
4260 DropEnable dropEnables[] = {\r
4261   { 'P', DP_Pawn, N_("Pawn") },\r
4262   { 'N', DP_Knight, N_("Knight") },\r
4263   { 'B', DP_Bishop, N_("Bishop") },\r
4264   { 'R', DP_Rook, N_("Rook") },\r
4265   { 'Q', DP_Queen, N_("Queen") },\r
4266 };\r
4267 \r
4268 VOID\r
4269 SetupDropMenu(HMENU hmenu)\r
4270 {\r
4271   int i, count, enable;\r
4272   char *p;\r
4273   extern char white_holding[], black_holding[];\r
4274   char item[MSG_SIZ];\r
4275 \r
4276   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4277     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4278                dropEnables[i].piece);\r
4279     count = 0;\r
4280     while (p && *p++ == dropEnables[i].piece) count++;\r
4281       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4282     enable = count > 0 || !appData.testLegality\r
4283       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4284                       && !appData.icsActive);\r
4285     ModifyMenu(hmenu, dropEnables[i].command,\r
4286                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4287                dropEnables[i].command, item);\r
4288   }\r
4289 }\r
4290 \r
4291 void DragPieceBegin(int x, int y, Boolean instantly)\r
4292 {\r
4293       dragInfo.lastpos.x = boardRect.left + x;\r
4294       dragInfo.lastpos.y = boardRect.top + y;\r
4295       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4296       dragInfo.from.x = fromX;\r
4297       dragInfo.from.y = fromY;\r
4298       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4299       dragInfo.start = dragInfo.from;\r
4300       SetCapture(hwndMain);\r
4301 }\r
4302 \r
4303 void DragPieceEnd(int x, int y)\r
4304 {\r
4305     ReleaseCapture();\r
4306     dragInfo.start.x = dragInfo.start.y = -1;\r
4307     dragInfo.from = dragInfo.start;\r
4308     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4309 }\r
4310 \r
4311 void ChangeDragPiece(ChessSquare piece)\r
4312 {\r
4313     dragInfo.piece = piece;\r
4314 }\r
4315 \r
4316 /* Event handler for mouse messages */\r
4317 VOID\r
4318 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4319 {\r
4320   int x, y, menuNr;\r
4321   POINT pt;\r
4322   static int recursive = 0;\r
4323   HMENU hmenu;\r
4324   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4325 \r
4326   if (recursive) {\r
4327     if (message == WM_MBUTTONUP) {\r
4328       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4329          to the middle button: we simulate pressing the left button too!\r
4330          */\r
4331       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4332       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4333     }\r
4334     return;\r
4335   }\r
4336   recursive++;\r
4337   \r
4338   pt.x = LOWORD(lParam);\r
4339   pt.y = HIWORD(lParam);\r
4340   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4341   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4342   if (!flipView && y >= 0) {\r
4343     y = BOARD_HEIGHT - 1 - y;\r
4344   }\r
4345   if (flipView && x >= 0) {\r
4346     x = BOARD_WIDTH - 1 - x;\r
4347   }\r
4348 \r
4349   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4350   controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status\r
4351 \r
4352   switch (message) {\r
4353   case WM_LBUTTONDOWN:\r
4354       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4355         ClockClick(flipClock); break;\r
4356       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4357         ClockClick(!flipClock); break;\r
4358       }\r
4359     if(dragging) { // [HGM] lion: don't destroy dragging info if we are already dragging\r
4360       dragInfo.start.x = dragInfo.start.y = -1;\r
4361       dragInfo.from = dragInfo.start;\r
4362     }\r
4363     if(fromX == -1 && frozen) { // not sure where this is for\r
4364                 fromX = fromY = -1; \r
4365       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4366       break;\r
4367     }\r
4368       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4369       DrawPosition(TRUE, NULL);\r
4370     break;\r
4371 \r
4372   case WM_LBUTTONUP:\r
4373       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4374       DrawPosition(TRUE, NULL);\r
4375     break;\r
4376 \r
4377   case WM_MOUSEMOVE:\r
4378     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4379     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4380     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4381     if ((appData.animateDragging || appData.highlightDragging)\r
4382         && (wParam & MK_LBUTTON || dragging == 2)\r
4383         && dragInfo.from.x >= 0) \r
4384     {\r
4385       BOOL full_repaint = FALSE;\r
4386 \r
4387       if (appData.animateDragging) {\r
4388         dragInfo.pos = pt;\r
4389       }\r
4390       if (appData.highlightDragging) {\r
4391         HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);\r
4392         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4393             full_repaint = TRUE;\r
4394         }\r
4395       }\r
4396       \r
4397       DrawPosition( full_repaint, NULL);\r
4398       \r
4399       dragInfo.lastpos = dragInfo.pos;\r
4400     }\r
4401     break;\r
4402 \r
4403   case WM_MOUSEWHEEL: // [DM]\r
4404     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4405        /* Mouse Wheel is being rolled forward\r
4406         * Play moves forward\r
4407         */\r
4408        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4409                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4410        /* Mouse Wheel is being rolled backward\r
4411         * Play moves backward\r
4412         */\r
4413        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4414                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4415     }\r
4416     break;\r
4417 \r
4418   case WM_MBUTTONUP:\r
4419   case WM_RBUTTONUP:\r
4420     ReleaseCapture();\r
4421     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4422     break;\r
4423  \r
4424   case WM_MBUTTONDOWN:\r
4425   case WM_RBUTTONDOWN:\r
4426     ErrorPopDown();\r
4427     ReleaseCapture();\r
4428     fromX = fromY = -1;\r
4429     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4430     dragInfo.start.x = dragInfo.start.y = -1;\r
4431     dragInfo.from = dragInfo.start;\r
4432     dragInfo.lastpos = dragInfo.pos;\r
4433     if (appData.highlightDragging) {\r
4434       ClearHighlights();\r
4435     }\r
4436     if(y == -2) {\r
4437       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4438       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4439           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4440       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4441           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4442       }\r
4443       break;\r
4444     }\r
4445     DrawPosition(TRUE, NULL);\r
4446 \r
4447     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4448     switch (menuNr) {\r
4449     case 0:\r
4450       if (message == WM_MBUTTONDOWN) {\r
4451         buttonCount = 3;  /* even if system didn't think so */\r
4452         if (wParam & MK_SHIFT) \r
4453           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4454         else\r
4455           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4456       } else { /* message == WM_RBUTTONDOWN */\r
4457         /* Just have one menu, on the right button.  Windows users don't\r
4458            think to try the middle one, and sometimes other software steals\r
4459            it, or it doesn't really exist. */\r
4460         if(gameInfo.variant != VariantShogi)\r
4461             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4462         else\r
4463             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4464       }\r
4465       break;\r
4466     case 2:\r
4467       SetCapture(hwndMain);\r
4468       break;\r
4469     case 1:\r
4470       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4471       SetupDropMenu(hmenu);\r
4472       MenuPopup(hwnd, pt, hmenu, -1);\r
4473     default:\r
4474       break;\r
4475     }\r
4476     break;\r
4477   }\r
4478 \r
4479   recursive--;\r
4480 }\r
4481 \r
4482 /* Preprocess messages for buttons in main window */\r
4483 LRESULT CALLBACK\r
4484 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4485 {\r
4486   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4487   int i, dir;\r
4488 \r
4489   for (i=0; i<N_BUTTONS; i++) {\r
4490     if (buttonDesc[i].id == id) break;\r
4491   }\r
4492   if (i == N_BUTTONS) return 0;\r
4493   switch (message) {\r
4494   case WM_KEYDOWN:\r
4495     switch (wParam) {\r
4496     case VK_LEFT:\r
4497     case VK_RIGHT:\r
4498       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4499       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4500       return TRUE;\r
4501     }\r
4502     break;\r
4503   case WM_CHAR:\r
4504     switch (wParam) {\r
4505     case '\r':\r
4506       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4507       return TRUE;\r
4508     default:\r
4509       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4510         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4511         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4512         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4513         SetFocus(h);\r
4514         SendMessage(h, WM_CHAR, wParam, lParam);\r
4515         return TRUE;\r
4516       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4517         TypeInEvent((char)wParam);\r
4518       }\r
4519       break;\r
4520     }\r
4521     break;\r
4522   }\r
4523   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4524 }\r
4525 \r
4526 static int promoStyle;\r
4527 \r
4528 /* Process messages for Promotion dialog box */\r
4529 LRESULT CALLBACK\r
4530 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4531 {\r
4532 \r
4533   char promoChar;\r
4534 \r
4535   switch (message) {\r
4536 \r
4537   case WM_INITDIALOG: /* message: initialize dialog box */\r
4538     /* Center the dialog over the application window */\r
4539     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4540     Translate(hDlg, DLG_PromotionKing);\r
4541     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4542       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4543        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4544        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4545                SW_SHOW : SW_HIDE);\r
4546     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4547     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4548        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4549          PieceToChar(WhiteAngel) != '~') ||\r
4550         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4551          PieceToChar(BlackAngel) != '~')   ) ?\r
4552                SW_SHOW : SW_HIDE);\r
4553     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4554        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4555          PieceToChar(WhiteMarshall) != '~') ||\r
4556         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4557          PieceToChar(BlackMarshall) != '~')   ) ?\r
4558                SW_SHOW : SW_HIDE);\r
4559     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4560     ShowWindow(GetDlgItem(hDlg, PB_Rook),   !promoStyle ? SW_SHOW : SW_HIDE);\r
4561     ShowWindow(GetDlgItem(hDlg, PB_Bishop), !promoStyle ? SW_SHOW : SW_HIDE);\r
4562     if(promoStyle) {\r
4563         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4564         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4565         SetWindowText(hDlg, "Promote?");\r
4566     }\r
4567     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4568        gameInfo.variant == VariantSuper ?\r
4569                SW_SHOW : SW_HIDE);\r
4570     return TRUE;\r
4571 \r
4572   case WM_COMMAND: /* message: received a command */\r
4573     switch (LOWORD(wParam)) {\r
4574     case IDCANCEL:\r
4575       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4576       ClearHighlights();\r
4577       DrawPosition(FALSE, NULL);\r
4578       return TRUE;\r
4579     case PB_King:\r
4580       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4581       break;\r
4582     case PB_Queen:\r
4583       promoChar = promoStyle ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4584       break;\r
4585     case PB_Rook:\r
4586       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4587       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4588       break;\r
4589     case PB_Bishop:\r
4590       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4591       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4592       break;\r
4593     case PB_Chancellor:\r
4594       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4595       break;\r
4596     case PB_Archbishop:\r
4597       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4598       break;\r
4599     case PB_Knight:\r
4600       promoChar = gameInfo.variant == VariantShogi ? '=' : promoStyle ? NULLCHAR : \r
4601                   ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight));\r
4602       break;\r
4603     default:\r
4604       return FALSE;\r
4605     }\r
4606     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4607     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4608     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4609     fromX = fromY = -1;\r
4610     if (!appData.highlightLastMove) {\r
4611       ClearHighlights();\r
4612       DrawPosition(FALSE, NULL);\r
4613     }\r
4614     return TRUE;\r
4615   }\r
4616   return FALSE;\r
4617 }\r
4618 \r
4619 /* Pop up promotion dialog */\r
4620 VOID\r
4621 PromotionPopup(HWND hwnd)\r
4622 {\r
4623   FARPROC lpProc;\r
4624 \r
4625   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4626   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4627     hwnd, (DLGPROC)lpProc);\r
4628   FreeProcInstance(lpProc);\r
4629 }\r
4630 \r
4631 void\r
4632 PromotionPopUp(char choice)\r
4633 {\r
4634   promoStyle = (choice == '+' || IS_SHOGI(gameInfo.variant));\r
4635   DrawPosition(TRUE, NULL);\r
4636   PromotionPopup(hwndMain);\r
4637 }\r
4638 \r
4639 VOID\r
4640 LoadGameDialog(HWND hwnd, char* title)\r
4641 {\r
4642   UINT number = 0;\r
4643   FILE *f;\r
4644   char fileTitle[MSG_SIZ];\r
4645   f = OpenFileDialog(hwnd, "rb", "",\r
4646                      appData.oldSaveStyle ? "gam" : "pgn",\r
4647                      GAME_FILT,\r
4648                      title, &number, fileTitle, NULL);\r
4649   if (f != NULL) {\r
4650     cmailMsgLoaded = FALSE;\r
4651     if (number == 0) {\r
4652       int error = GameListBuild(f);\r
4653       if (error) {\r
4654         DisplayError(_("Cannot build game list"), error);\r
4655       } else if (!ListEmpty(&gameList) &&\r
4656                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4657         GameListPopUp(f, fileTitle);\r
4658         return;\r
4659       }\r
4660       GameListDestroy();\r
4661       number = 1;\r
4662     }\r
4663     LoadGame(f, number, fileTitle, FALSE);\r
4664   }\r
4665 }\r
4666 \r
4667 int get_term_width()\r
4668 {\r
4669     HDC hdc;\r
4670     TEXTMETRIC tm;\r
4671     RECT rc;\r
4672     HFONT hfont, hold_font;\r
4673     LOGFONT lf;\r
4674     HWND hText;\r
4675 \r
4676     if (hwndConsole)\r
4677         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4678     else\r
4679         return 79;\r
4680 \r
4681     // get the text metrics\r
4682     hdc = GetDC(hText);\r
4683     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4684     if (consoleCF.dwEffects & CFE_BOLD)\r
4685         lf.lfWeight = FW_BOLD;\r
4686     if (consoleCF.dwEffects & CFE_ITALIC)\r
4687         lf.lfItalic = TRUE;\r
4688     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4689         lf.lfStrikeOut = TRUE;\r
4690     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4691         lf.lfUnderline = TRUE;\r
4692     hfont = CreateFontIndirect(&lf);\r
4693     hold_font = SelectObject(hdc, hfont);\r
4694     GetTextMetrics(hdc, &tm);\r
4695     SelectObject(hdc, hold_font);\r
4696     DeleteObject(hfont);\r
4697     ReleaseDC(hText, hdc);\r
4698 \r
4699     // get the rectangle\r
4700     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4701 \r
4702     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4703 }\r
4704 \r
4705 void UpdateICSWidth(HWND hText)\r
4706 {\r
4707     LONG old_width, new_width;\r
4708 \r
4709     new_width = get_term_width(hText, FALSE);\r
4710     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4711     if (new_width != old_width)\r
4712     {\r
4713         ics_update_width(new_width);\r
4714         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4715     }\r
4716 }\r
4717 \r
4718 VOID\r
4719 ChangedConsoleFont()\r
4720 {\r
4721   CHARFORMAT cfmt;\r
4722   CHARRANGE tmpsel, sel;\r
4723   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4724   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4725   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4726   PARAFORMAT paraf;\r
4727 \r
4728   cfmt.cbSize = sizeof(CHARFORMAT);\r
4729   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4730     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4731                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4732   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4733    * size.  This was undocumented in the version of MSVC++ that I had\r
4734    * when I wrote the code, but is apparently documented now.\r
4735    */\r
4736   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4737   cfmt.bCharSet = f->lf.lfCharSet;\r
4738   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4739   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4740   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4741   /* Why are the following seemingly needed too? */\r
4742   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4743   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4744   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4745   tmpsel.cpMin = 0;\r
4746   tmpsel.cpMax = -1; /*999999?*/\r
4747   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4748   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4749   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4750    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4751    */\r
4752   paraf.cbSize = sizeof(paraf);\r
4753   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4754   paraf.dxStartIndent = 0;\r
4755   paraf.dxOffset = WRAP_INDENT;\r
4756   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4757   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4758   UpdateICSWidth(hText);\r
4759 }\r
4760 \r
4761 /*---------------------------------------------------------------------------*\\r
4762  *\r
4763  * Window Proc for main window\r
4764  *\r
4765 \*---------------------------------------------------------------------------*/\r
4766 \r
4767 /* Process messages for main window, etc. */\r
4768 LRESULT CALLBACK\r
4769 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4770 {\r
4771   FARPROC lpProc;\r
4772   int wmId;\r
4773   char *defName;\r
4774   FILE *f;\r
4775   UINT number;\r
4776   char fileTitle[MSG_SIZ];\r
4777   static SnapData sd;\r
4778   static int peek=0;\r
4779 \r
4780   switch (message) {\r
4781 \r
4782   case WM_PAINT: /* message: repaint portion of window */\r
4783     PaintProc(hwnd);\r
4784     break;\r
4785 \r
4786   case WM_ERASEBKGND:\r
4787     if (IsIconic(hwnd)) {\r
4788       /* Cheat; change the message */\r
4789       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4790     } else {\r
4791       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4792     }\r
4793     break;\r
4794 \r
4795   case WM_LBUTTONDOWN:\r
4796   case WM_MBUTTONDOWN:\r
4797   case WM_RBUTTONDOWN:\r
4798   case WM_LBUTTONUP:\r
4799   case WM_MBUTTONUP:\r
4800   case WM_RBUTTONUP:\r
4801   case WM_MOUSEMOVE:\r
4802   case WM_MOUSEWHEEL:\r
4803     MouseEvent(hwnd, message, wParam, lParam);\r
4804     break;\r
4805 \r
4806   case WM_KEYUP:\r
4807     if((char)wParam == '\b') {\r
4808       ForwardEvent(); peek = 0;\r
4809     }\r
4810 \r
4811     JAWS_KBUP_NAVIGATION\r
4812 \r
4813     break;\r
4814 \r
4815   case WM_KEYDOWN:\r
4816     if((char)wParam == '\b') {\r
4817       if(!peek) BackwardEvent(), peek = 1;\r
4818     }\r
4819 \r
4820     JAWS_KBDOWN_NAVIGATION\r
4821 \r
4822     break;\r
4823 \r
4824   case WM_CHAR:\r
4825     \r
4826     JAWS_ALT_INTERCEPT\r
4827 \r
4828     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4829         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4830         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4831         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4832         SetFocus(h);\r
4833         SendMessage(h, message, wParam, lParam);\r
4834     } else if(lParam != KF_REPEAT) {\r
4835         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4836                 TypeInEvent((char)wParam);\r
4837         } else if((char)wParam == 003) CopyGameToClipboard();\r
4838          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4839     }\r
4840 \r
4841     break;\r
4842 \r
4843   case WM_PALETTECHANGED:\r
4844     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4845       int nnew;\r
4846       HDC hdc = GetDC(hwndMain);\r
4847       SelectPalette(hdc, hPal, TRUE);\r
4848       nnew = RealizePalette(hdc);\r
4849       if (nnew > 0) {\r
4850         paletteChanged = TRUE;\r
4851 \r
4852         InvalidateRect(hwnd, &boardRect, FALSE);\r
4853       }\r
4854       ReleaseDC(hwnd, hdc);\r
4855     }\r
4856     break;\r
4857 \r
4858   case WM_QUERYNEWPALETTE:\r
4859     if (!appData.monoMode /*&& paletteChanged*/) {\r
4860       int nnew;\r
4861       HDC hdc = GetDC(hwndMain);\r
4862       paletteChanged = FALSE;\r
4863       SelectPalette(hdc, hPal, FALSE);\r
4864       nnew = RealizePalette(hdc);\r
4865       if (nnew > 0) {\r
4866         InvalidateRect(hwnd, &boardRect, FALSE);\r
4867       }\r
4868       ReleaseDC(hwnd, hdc);\r
4869       return TRUE;\r
4870     }\r
4871     return FALSE;\r
4872 \r
4873   case WM_COMMAND: /* message: command from application menu */\r
4874     wmId    = LOWORD(wParam);\r
4875 \r
4876     switch (wmId) {\r
4877     case IDM_NewGame:\r
4878       ResetGameEvent();\r
4879       SAY("new game enter a move to play against the computer with white");\r
4880       break;\r
4881 \r
4882     case IDM_NewGameFRC:\r
4883       if( NewGameFRC() == 0 ) {\r
4884         ResetGameEvent();\r
4885       }\r
4886       break;\r
4887 \r
4888     case IDM_NewVariant:\r
4889       NewVariantPopup(hwnd);\r
4890       break;\r
4891 \r
4892     case IDM_LoadGame:\r
4893       LoadGameDialog(hwnd, _("Load Game from File"));\r
4894       break;\r
4895 \r
4896     case IDM_LoadNextGame:\r
4897       ReloadGame(1);\r
4898       break;\r
4899 \r
4900     case IDM_LoadPrevGame:\r
4901       ReloadGame(-1);\r
4902       break;\r
4903 \r
4904     case IDM_ReloadGame:\r
4905       ReloadGame(0);\r
4906       break;\r
4907 \r
4908     case IDM_LoadPosition:\r
4909       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4910         Reset(FALSE, TRUE);\r
4911       }\r
4912       number = 1;\r
4913       f = OpenFileDialog(hwnd, "rb", "",\r
4914                          appData.oldSaveStyle ? "pos" : "fen",\r
4915                          POSITION_FILT,\r
4916                          _("Load Position from File"), &number, fileTitle, NULL);\r
4917       if (f != NULL) {\r
4918         LoadPosition(f, number, fileTitle);\r
4919       }\r
4920       break;\r
4921 \r
4922     case IDM_LoadNextPosition:\r
4923       ReloadPosition(1);\r
4924       break;\r
4925 \r
4926     case IDM_LoadPrevPosition:\r
4927       ReloadPosition(-1);\r
4928       break;\r
4929 \r
4930     case IDM_ReloadPosition:\r
4931       ReloadPosition(0);\r
4932       break;\r
4933 \r
4934     case IDM_SaveGame:\r
4935       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4936       f = OpenFileDialog(hwnd, "a", defName,\r
4937                          appData.oldSaveStyle ? "gam" : "pgn",\r
4938                          GAME_FILT,\r
4939                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4940       if (f != NULL) {\r
4941         SaveGame(f, 0, "");\r
4942       }\r
4943       break;\r
4944 \r
4945     case IDM_SavePosition:\r
4946       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4947       f = OpenFileDialog(hwnd, "a", defName,\r
4948                          appData.oldSaveStyle ? "pos" : "fen",\r
4949                          POSITION_FILT,\r
4950                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4951       if (f != NULL) {\r
4952         SavePosition(f, 0, "");\r
4953       }\r
4954       break;\r
4955 \r
4956     case IDM_SaveDiagram:\r
4957       defName = "diagram";\r
4958       f = OpenFileDialog(hwnd, "wb", defName,\r
4959                          "bmp",\r
4960                          DIAGRAM_FILT,\r
4961                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4962       if (f != NULL) {\r
4963         SaveDiagram(f);\r
4964       }\r
4965       break;\r
4966 \r
4967     case IDM_SaveSelected:\r
4968       f = OpenFileDialog(hwnd, "a", "",\r
4969                          "pgn",\r
4970                          GAME_FILT,\r
4971                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4972       if (f != NULL) {\r
4973         SaveSelected(f, 0, "");\r
4974       }\r
4975       break;\r
4976 \r
4977     case IDM_CreateBook:\r
4978       CreateBookEvent();\r
4979       break;\r
4980 \r
4981     case IDM_CopyGame:\r
4982       CopyGameToClipboard();\r
4983       break;\r
4984 \r
4985     case IDM_PasteGame:\r
4986       PasteGameFromClipboard();\r
4987       break;\r
4988 \r
4989     case IDM_CopyGameListToClipboard:\r
4990       CopyGameListToClipboard();\r
4991       break;\r
4992 \r
4993     /* [AS] Autodetect FEN or PGN data */\r
4994     case IDM_PasteAny:\r
4995       PasteGameOrFENFromClipboard();\r
4996       break;\r
4997 \r
4998     /* [AS] Move history */\r
4999     case IDM_ShowMoveHistory:\r
5000         if( MoveHistoryIsUp() ) {\r
5001             MoveHistoryPopDown();\r
5002         }\r
5003         else {\r
5004             MoveHistoryPopUp();\r
5005         }\r
5006         break;\r
5007 \r
5008     /* [AS] Eval graph */\r
5009     case IDM_ShowEvalGraph:\r
5010         if( EvalGraphIsUp() ) {\r
5011             EvalGraphPopDown();\r
5012         }\r
5013         else {\r
5014             EvalGraphPopUp();\r
5015             SetFocus(hwndMain);\r
5016         }\r
5017         break;\r
5018 \r
5019     /* [AS] Engine output */\r
5020     case IDM_ShowEngineOutput:\r
5021         if( EngineOutputIsUp() ) {\r
5022             EngineOutputPopDown();\r
5023         }\r
5024         else {\r
5025             EngineOutputPopUp();\r
5026         }\r
5027         break;\r
5028 \r
5029     /* [AS] User adjudication */\r
5030     case IDM_UserAdjudication_White:\r
5031         UserAdjudicationEvent( +1 );\r
5032         break;\r
5033 \r
5034     case IDM_UserAdjudication_Black:\r
5035         UserAdjudicationEvent( -1 );\r
5036         break;\r
5037 \r
5038     case IDM_UserAdjudication_Draw:\r
5039         UserAdjudicationEvent( 0 );\r
5040         break;\r
5041 \r
5042     /* [AS] Game list options dialog */\r
5043     case IDM_GameListOptions:\r
5044       GameListOptions();\r
5045       break;\r
5046 \r
5047     case IDM_NewChat:\r
5048       ChatPopUp(NULL);\r
5049       break;\r
5050 \r
5051     case IDM_CopyPosition:\r
5052       CopyFENToClipboard();\r
5053       break;\r
5054 \r
5055     case IDM_PastePosition:\r
5056       PasteFENFromClipboard();\r
5057       break;\r
5058 \r
5059     case IDM_MailMove:\r
5060       MailMoveEvent();\r
5061       break;\r
5062 \r
5063     case IDM_ReloadCMailMsg:\r
5064       Reset(TRUE, TRUE);\r
5065       ReloadCmailMsgEvent(FALSE);\r
5066       break;\r
5067 \r
5068     case IDM_Minimize:\r
5069       ShowWindow(hwnd, SW_MINIMIZE);\r
5070       break;\r
5071 \r
5072     case IDM_Exit:\r
5073       ExitEvent(0);\r
5074       break;\r
5075 \r
5076     case IDM_MachineWhite:\r
5077       MachineWhiteEvent();\r
5078       /*\r
5079        * refresh the tags dialog only if it's visible\r
5080        */\r
5081       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5082           char *tags;\r
5083           tags = PGNTags(&gameInfo);\r
5084           TagsPopUp(tags, CmailMsg());\r
5085           free(tags);\r
5086       }\r
5087       SAY("computer starts playing white");\r
5088       break;\r
5089 \r
5090     case IDM_MachineBlack:\r
5091       MachineBlackEvent();\r
5092       /*\r
5093        * refresh the tags dialog only if it's visible\r
5094        */\r
5095       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5096           char *tags;\r
5097           tags = PGNTags(&gameInfo);\r
5098           TagsPopUp(tags, CmailMsg());\r
5099           free(tags);\r
5100       }\r
5101       SAY("computer starts playing black");\r
5102       break;\r
5103 \r
5104     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
5105       if(matchMode) EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_GRAYED);\r
5106       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
5107       break;\r
5108 \r
5109     case IDM_TwoMachines:\r
5110       TwoMachinesEvent();\r
5111       /*\r
5112 \r
5113        * refresh the tags dialog only if it's visible\r
5114        */\r
5115       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5116           char *tags;\r
5117           tags = PGNTags(&gameInfo);\r
5118           TagsPopUp(tags, CmailMsg());\r
5119           free(tags);\r
5120       }\r
5121       SAY("computer starts playing both sides");\r
5122       break;\r
5123 \r
5124     case IDM_AnalysisMode:\r
5125       if(AnalyzeModeEvent()) {\r
5126         SAY("analyzing current position");\r
5127       }\r
5128       break;\r
5129 \r
5130     case IDM_AnalyzeFile:\r
5131       AnalyzeFileEvent();\r
5132       break;\r
5133 \r
5134     case IDM_IcsClient:\r
5135       IcsClientEvent();\r
5136       break;\r
5137 \r
5138     case IDM_EditGame:\r
5139     case IDM_EditGame2:\r
5140       EditGameEvent();\r
5141       SAY("edit game");\r
5142       break;\r
5143 \r
5144     case IDM_EditPosition:\r
5145     case IDM_EditPosition2:\r
5146       EditPositionEvent();\r
5147       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
5148       break;\r
5149 \r
5150     case IDM_Training:\r
5151       TrainingEvent();\r
5152       break;\r
5153 \r
5154     case IDM_ShowGameList:\r
5155       ShowGameListProc();\r
5156       break;\r
5157 \r
5158     case IDM_EditProgs1:\r
5159       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
5160       break;\r
5161 \r
5162     case IDM_LoadProg1:\r
5163      LoadEnginePopUp(hwndMain, 0);\r
5164       break;\r
5165 \r
5166     case IDM_LoadProg2:\r
5167      LoadEnginePopUp(hwndMain, 1);\r
5168       break;\r
5169 \r
5170     case IDM_EditServers:\r
5171       EditTagsPopUp(icsNames, &icsNames);\r
5172       break;\r
5173 \r
5174     case IDM_EditTags:\r
5175     case IDM_Tags:\r
5176       EditTagsProc();\r
5177       break;\r
5178 \r
5179     case IDM_EditBook:\r
5180       EditBookEvent();\r
5181       break;\r
5182 \r
5183     case IDM_EditComment:\r
5184     case IDM_Comment:\r
5185       if (commentUp && editComment) {\r
5186         CommentPopDown();\r
5187       } else {\r
5188         EditCommentEvent();\r
5189       }\r
5190       break;\r
5191 \r
5192     case IDM_Pause:\r
5193       PauseEvent();\r
5194       break;\r
5195 \r
5196     case IDM_Accept:\r
5197       AcceptEvent();\r
5198       break;\r
5199 \r
5200     case IDM_Decline:\r
5201       DeclineEvent();\r
5202       break;\r
5203 \r
5204     case IDM_Rematch:\r
5205 \r
5206       RematchEvent();\r
5207       break;\r
5208 \r
5209     case IDM_CallFlag:\r
5210       CallFlagEvent();\r
5211       break;\r
5212 \r
5213     case IDM_Draw:\r
5214       DrawEvent();\r
5215       break;\r
5216 \r
5217     case IDM_Adjourn:\r
5218       AdjournEvent();\r
5219       break;\r
5220 \r
5221     case IDM_Abort:\r
5222       AbortEvent();\r
5223       break;\r
5224 \r
5225     case IDM_Resign:\r
5226       ResignEvent();\r
5227       break;\r
5228 \r
5229     case IDM_StopObserving:\r
5230       StopObservingEvent();\r
5231       break;\r
5232 \r
5233     case IDM_StopExamining:\r
5234       StopExaminingEvent();\r
5235       break;\r
5236 \r
5237     case IDM_Upload:\r
5238       UploadGameEvent();\r
5239       break;\r
5240 \r
5241     case IDM_TypeInMove:\r
5242       TypeInEvent('\000');\r
5243       break;\r
5244 \r
5245     case IDM_TypeInName:\r
5246       PopUpNameDialog('\000');\r
5247       break;\r
5248 \r
5249     case IDM_Backward:\r
5250       BackwardEvent();\r
5251       SetFocus(hwndMain);\r
5252       break;\r
5253 \r
5254     JAWS_MENU_ITEMS\r
5255 \r
5256     case IDM_Forward:\r
5257       ForwardEvent();\r
5258       SetFocus(hwndMain);\r
5259       break;\r
5260 \r
5261     case IDM_ToStart:\r
5262       ToStartEvent();\r
5263       SetFocus(hwndMain);\r
5264       break;\r
5265 \r
5266     case IDM_ToEnd:\r
5267       ToEndEvent();\r
5268       SetFocus(hwndMain);\r
5269       break;\r
5270 \r
5271     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5272     case OPT_GameListPrev:\r
5273       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5274       break;\r
5275 \r
5276     case IDM_Revert:\r
5277       RevertEvent(FALSE);\r
5278       break;\r
5279 \r
5280     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5281       RevertEvent(TRUE);\r
5282       break;\r
5283 \r
5284     case IDM_TruncateGame:\r
5285       TruncateGameEvent();\r
5286       break;\r
5287 \r
5288     case IDM_MoveNow:\r
5289       MoveNowEvent();\r
5290       break;\r
5291 \r
5292     case IDM_RetractMove:\r
5293       RetractMoveEvent();\r
5294       break;\r
5295 \r
5296     case IDM_FlipView:\r
5297       flipView = !flipView;\r
5298       DrawPosition(FALSE, NULL);\r
5299       break;\r
5300 \r
5301     case IDM_FlipClock:\r
5302       flipClock = !flipClock;\r
5303       DisplayBothClocks();\r
5304       DisplayLogos();\r
5305       break;\r
5306 \r
5307     case IDM_MuteSounds:\r
5308       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5309       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5310                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5311       break;\r
5312 \r
5313     case IDM_GeneralOptions:\r
5314       GeneralOptionsPopup(hwnd);\r
5315       DrawPosition(TRUE, NULL);\r
5316       break;\r
5317 \r
5318     case IDM_BoardOptions:\r
5319       BoardOptionsPopup(hwnd);\r
5320       break;\r
5321 \r
5322     case IDM_ThemeOptions:\r
5323       ThemeOptionsPopup(hwnd);\r
5324       break;\r
5325 \r
5326     case IDM_EnginePlayOptions:\r
5327       EnginePlayOptionsPopup(hwnd);\r
5328       break;\r
5329 \r
5330     case IDM_Engine1Options:\r
5331       EngineOptionsPopup(hwnd, &first);\r
5332       break;\r
5333 \r
5334     case IDM_Engine2Options:\r
5335       savedHwnd = hwnd;\r
5336       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5337       EngineOptionsPopup(hwnd, &second);\r
5338       break;\r
5339 \r
5340     case IDM_OptionsUCI:\r
5341       UciOptionsPopup(hwnd);\r
5342       break;\r
5343 \r
5344     case IDM_Tourney:\r
5345       TourneyPopup(hwnd);\r
5346       break;\r
5347 \r
5348     case IDM_IcsOptions:\r
5349       IcsOptionsPopup(hwnd);\r
5350       break;\r
5351 \r
5352     case IDM_Fonts:\r
5353       FontsOptionsPopup(hwnd);\r
5354       break;\r
5355 \r
5356     case IDM_Sounds:\r
5357       SoundOptionsPopup(hwnd);\r
5358       break;\r
5359 \r
5360     case IDM_CommPort:\r
5361       CommPortOptionsPopup(hwnd);\r
5362       break;\r
5363 \r
5364     case IDM_LoadOptions:\r
5365       LoadOptionsPopup(hwnd);\r
5366       break;\r
5367 \r
5368     case IDM_SaveOptions:\r
5369       SaveOptionsPopup(hwnd);\r
5370       break;\r
5371 \r
5372     case IDM_TimeControl:\r
5373       TimeControlOptionsPopup(hwnd);\r
5374       break;\r
5375 \r
5376     case IDM_SaveSettings:\r
5377       SaveSettings(settingsFileName);\r
5378       break;\r
5379 \r
5380     case IDM_SaveSettingsOnExit:\r
5381       saveSettingsOnExit = !saveSettingsOnExit;\r
5382       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5383                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5384                                          MF_CHECKED : MF_UNCHECKED));\r
5385       break;\r
5386 \r
5387     case IDM_Hint:\r
5388       HintEvent();\r
5389       break;\r
5390 \r
5391     case IDM_Book:\r
5392       BookEvent();\r
5393       break;\r
5394 \r
5395     case IDM_AboutGame:\r
5396       AboutGameEvent();\r
5397       break;\r
5398 \r
5399     case IDM_Debug:\r
5400       appData.debugMode = !appData.debugMode;\r
5401       if (appData.debugMode) {\r
5402         char dir[MSG_SIZ];\r
5403         GetCurrentDirectory(MSG_SIZ, dir);\r
5404         SetCurrentDirectory(installDir);\r
5405         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5406         SetCurrentDirectory(dir);\r
5407         setbuf(debugFP, NULL);\r
5408       } else {\r
5409         fclose(debugFP);\r
5410         debugFP = NULL;\r
5411       }\r
5412       break;\r
5413 \r
5414     case IDM_HELPCONTENTS:\r
5415       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5416           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5417           MessageBox (GetFocus(),\r
5418                     _("Unable to activate help"),\r
5419                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5420       }\r
5421       break;\r
5422 \r
5423     case IDM_HELPSEARCH:\r
5424         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5425             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5426         MessageBox (GetFocus(),\r
5427                     _("Unable to activate help"),\r
5428                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5429       }\r
5430       break;\r
5431 \r
5432     case IDM_HELPHELP:\r
5433       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5434         MessageBox (GetFocus(),\r
5435                     _("Unable to activate help"),\r
5436                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5437       }\r
5438       break;\r
5439 \r
5440     case IDM_ABOUT:\r
5441       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5442       DialogBox(hInst, \r
5443         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5444         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5445       FreeProcInstance(lpProc);\r
5446       break;\r
5447 \r
5448     case IDM_DirectCommand1:\r
5449       AskQuestionEvent(_("Direct Command"),\r
5450                        _("Send to chess program:"), "", "1");\r
5451       break;\r
5452     case IDM_DirectCommand2:\r
5453       AskQuestionEvent(_("Direct Command"),\r
5454                        _("Send to second chess program:"), "", "2");\r
5455       break;\r
5456 \r
5457     case EP_WhitePawn:\r
5458       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5459       fromX = fromY = -1;\r
5460       break;\r
5461 \r
5462     case EP_WhiteKnight:\r
5463       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5464       fromX = fromY = -1;\r
5465       break;\r
5466 \r
5467     case EP_WhiteBishop:\r
5468       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5469       fromX = fromY = -1;\r
5470       break;\r
5471 \r
5472     case EP_WhiteRook:\r
5473       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5474       fromX = fromY = -1;\r
5475       break;\r
5476 \r
5477     case EP_WhiteQueen:\r
5478       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5479       fromX = fromY = -1;\r
5480       break;\r
5481 \r
5482     case EP_WhiteFerz:\r
5483       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5484       fromX = fromY = -1;\r
5485       break;\r
5486 \r
5487     case EP_WhiteWazir:\r
5488       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5489       fromX = fromY = -1;\r
5490       break;\r
5491 \r
5492     case EP_WhiteAlfil:\r
5493       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5494       fromX = fromY = -1;\r
5495       break;\r
5496 \r
5497     case EP_WhiteCannon:\r
5498       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5499       fromX = fromY = -1;\r
5500       break;\r
5501 \r
5502     case EP_WhiteCardinal:\r
5503       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5504       fromX = fromY = -1;\r
5505       break;\r
5506 \r
5507     case EP_WhiteMarshall:\r
5508       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5509       fromX = fromY = -1;\r
5510       break;\r
5511 \r
5512     case EP_WhiteKing:\r
5513       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5514       fromX = fromY = -1;\r
5515       break;\r
5516 \r
5517     case EP_BlackPawn:\r
5518       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5519       fromX = fromY = -1;\r
5520       break;\r
5521 \r
5522     case EP_BlackKnight:\r
5523       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5524       fromX = fromY = -1;\r
5525       break;\r
5526 \r
5527     case EP_BlackBishop:\r
5528       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5529       fromX = fromY = -1;\r
5530       break;\r
5531 \r
5532     case EP_BlackRook:\r
5533       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5534       fromX = fromY = -1;\r
5535       break;\r
5536 \r
5537     case EP_BlackQueen:\r
5538       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5539       fromX = fromY = -1;\r
5540       break;\r
5541 \r
5542     case EP_BlackFerz:\r
5543       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5544       fromX = fromY = -1;\r
5545       break;\r
5546 \r
5547     case EP_BlackWazir:\r
5548       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5549       fromX = fromY = -1;\r
5550       break;\r
5551 \r
5552     case EP_BlackAlfil:\r
5553       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5554       fromX = fromY = -1;\r
5555       break;\r
5556 \r
5557     case EP_BlackCannon:\r
5558       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5559       fromX = fromY = -1;\r
5560       break;\r
5561 \r
5562     case EP_BlackCardinal:\r
5563       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5564       fromX = fromY = -1;\r
5565       break;\r
5566 \r
5567     case EP_BlackMarshall:\r
5568       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5569       fromX = fromY = -1;\r
5570       break;\r
5571 \r
5572     case EP_BlackKing:\r
5573       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5574       fromX = fromY = -1;\r
5575       break;\r
5576 \r
5577     case EP_EmptySquare:\r
5578       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5579       fromX = fromY = -1;\r
5580       break;\r
5581 \r
5582     case EP_ClearBoard:\r
5583       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5584       fromX = fromY = -1;\r
5585       break;\r
5586 \r
5587     case EP_White:\r
5588       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5589       fromX = fromY = -1;\r
5590       break;\r
5591 \r
5592     case EP_Black:\r
5593       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5594       fromX = fromY = -1;\r
5595       break;\r
5596 \r
5597     case EP_Promote:\r
5598       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5599       fromX = fromY = -1;\r
5600       break;\r
5601 \r
5602     case EP_Demote:\r
5603       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5604       fromX = fromY = -1;\r
5605       break;\r
5606 \r
5607     case DP_Pawn:\r
5608       DropMenuEvent(WhitePawn, fromX, fromY);\r
5609       fromX = fromY = -1;\r
5610       break;\r
5611 \r
5612     case DP_Knight:\r
5613       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5614       fromX = fromY = -1;\r
5615       break;\r
5616 \r
5617     case DP_Bishop:\r
5618       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5619       fromX = fromY = -1;\r
5620       break;\r
5621 \r
5622     case DP_Rook:\r
5623       DropMenuEvent(WhiteRook, fromX, fromY);\r
5624       fromX = fromY = -1;\r
5625       break;\r
5626 \r
5627     case DP_Queen:\r
5628       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5629       fromX = fromY = -1;\r
5630       break;\r
5631 \r
5632     case IDM_English:\r
5633       barbaric = 0; appData.language = "";\r
5634       TranslateMenus(0);\r
5635       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5636       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5637       lastChecked = wmId;\r
5638       break;\r
5639 \r
5640     default:\r
5641       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5642           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5643       else\r
5644       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5645           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5646           TranslateMenus(0);\r
5647           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5648           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5649           lastChecked = wmId;\r
5650           break;\r
5651       }\r
5652       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5653     }\r
5654     break;\r
5655 \r
5656   case WM_TIMER:\r
5657     switch (wParam) {\r
5658     case CLOCK_TIMER_ID:\r
5659       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5660       clockTimerEvent = 0;\r
5661       DecrementClocks(); /* call into back end */\r
5662       break;\r
5663     case LOAD_GAME_TIMER_ID:\r
5664       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5665       loadGameTimerEvent = 0;\r
5666       AutoPlayGameLoop(); /* call into back end */\r
5667       break;\r
5668     case ANALYSIS_TIMER_ID:\r
5669       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5670                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5671         AnalysisPeriodicEvent(0);\r
5672       } else {\r
5673         KillTimer(hwnd, analysisTimerEvent);\r
5674         analysisTimerEvent = 0;\r
5675       }\r
5676       break;\r
5677     case DELAYED_TIMER_ID:\r
5678       KillTimer(hwnd, delayedTimerEvent);\r
5679       delayedTimerEvent = 0;\r
5680       delayedTimerCallback();\r
5681       break;\r
5682     }\r
5683     break;\r
5684 \r
5685   case WM_USER_Input:\r
5686     InputEvent(hwnd, message, wParam, lParam);\r
5687     break;\r
5688 \r
5689   /* [AS] Also move "attached" child windows */\r
5690   case WM_WINDOWPOSCHANGING:\r
5691 \r
5692     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5693         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5694 \r
5695         if( ((lpwp->flags & SWP_NOMOVE) == 0) /*&& ((lpwp->flags & SWP_NOSIZE) != 0)*/ ) { // [HGM] in Win8 size always accompanies move?\r
5696             /* Window is moving */\r
5697             RECT rcMain;\r
5698 \r
5699 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5700             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5701             rcMain.right  = wpMain.x + wpMain.width;\r
5702             rcMain.top    = wpMain.y;\r
5703             rcMain.bottom = wpMain.y + wpMain.height;\r
5704             \r
5705             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5706             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5707             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5708             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5709             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5710             wpMain.x = lpwp->x;\r
5711             wpMain.y = lpwp->y;\r
5712         }\r
5713     }\r
5714     break;\r
5715 \r
5716   /* [AS] Snapping */\r
5717   case WM_ENTERSIZEMOVE:\r
5718     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5719     if (hwnd == hwndMain) {\r
5720       doingSizing = TRUE;\r
5721       lastSizing = 0;\r
5722     }\r
5723     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5724     break;\r
5725 \r
5726   case WM_SIZING:\r
5727     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5728     if (hwnd == hwndMain) {\r
5729       lastSizing = wParam;\r
5730     }\r
5731     break;\r
5732 \r
5733   case WM_MOVING:\r
5734     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5735       return OnMoving( &sd, hwnd, wParam, lParam );\r
5736 \r
5737   case WM_EXITSIZEMOVE:\r
5738     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5739     if (hwnd == hwndMain) {\r
5740       RECT client;\r
5741       doingSizing = FALSE;\r
5742       InvalidateRect(hwnd, &boardRect, FALSE);\r
5743       GetClientRect(hwnd, &client);\r
5744       ResizeBoard(client.right, client.bottom, lastSizing);\r
5745       lastSizing = 0;\r
5746       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5747     }\r
5748     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5749     break;\r
5750 \r
5751   case WM_DESTROY: /* message: window being destroyed */\r
5752     PostQuitMessage(0);\r
5753     break;\r
5754 \r
5755   case WM_CLOSE:\r
5756     if (hwnd == hwndMain) {\r
5757       ExitEvent(0);\r
5758     }\r
5759     break;\r
5760 \r
5761   default:      /* Passes it on if unprocessed */\r
5762     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5763   }\r
5764 \r
5765 \r
5766   return 0;\r
5767 }\r
5768 \r
5769 /*---------------------------------------------------------------------------*\\r
5770  *\r
5771  * Misc utility routines\r
5772  *\r
5773 \*---------------------------------------------------------------------------*/\r
5774 \r
5775 /*\r
5776  * Decent random number generator, at least not as bad as Windows\r
5777  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5778  */\r
5779 unsigned int randstate;\r
5780 \r
5781 int\r
5782 myrandom(void)\r
5783 {\r
5784   randstate = randstate * 1664525 + 1013904223;\r
5785   return (int) randstate & 0x7fffffff;\r
5786 }\r
5787 \r
5788 void\r
5789 mysrandom(unsigned int seed)\r
5790 {\r
5791   randstate = seed;\r
5792 }\r
5793 \r
5794 \r
5795 /* \r
5796  * returns TRUE if user selects a different color, FALSE otherwise \r
5797  */\r
5798 \r
5799 BOOL\r
5800 ChangeColor(HWND hwnd, COLORREF *which)\r
5801 {\r
5802   static BOOL firstTime = TRUE;\r
5803   static DWORD customColors[16];\r
5804   CHOOSECOLOR cc;\r
5805   COLORREF newcolor;\r
5806   int i;\r
5807   ColorClass ccl;\r
5808 \r
5809   if (firstTime) {\r
5810     /* Make initial colors in use available as custom colors */\r
5811     /* Should we put the compiled-in defaults here instead? */\r
5812     i = 0;\r
5813     customColors[i++] = lightSquareColor & 0xffffff;\r
5814     customColors[i++] = darkSquareColor & 0xffffff;\r
5815     customColors[i++] = whitePieceColor & 0xffffff;\r
5816     customColors[i++] = blackPieceColor & 0xffffff;\r
5817     customColors[i++] = highlightSquareColor & 0xffffff;\r
5818     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5819 \r
5820     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5821       customColors[i++] = textAttribs[ccl].color;\r
5822     }\r
5823     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5824     firstTime = FALSE;\r
5825   }\r
5826 \r
5827   cc.lStructSize = sizeof(cc);\r
5828   cc.hwndOwner = hwnd;\r
5829   cc.hInstance = NULL;\r
5830   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5831   cc.lpCustColors = (LPDWORD) customColors;\r
5832   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5833 \r
5834   if (!ChooseColor(&cc)) return FALSE;\r
5835 \r
5836   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5837   if (newcolor == *which) return FALSE;\r
5838   *which = newcolor;\r
5839   return TRUE;\r
5840 \r
5841   /*\r
5842   InitDrawingColors();\r
5843   InvalidateRect(hwnd, &boardRect, FALSE);\r
5844   */\r
5845 }\r
5846 \r
5847 BOOLEAN\r
5848 MyLoadSound(MySound *ms)\r
5849 {\r
5850   BOOL ok = FALSE;\r
5851   struct stat st;\r
5852   FILE *f;\r
5853 \r
5854   if (ms->data && ms->flag) free(ms->data);\r
5855   ms->data = NULL;\r
5856 \r
5857   switch (ms->name[0]) {\r
5858   case NULLCHAR:\r
5859     /* Silence */\r
5860     ok = TRUE;\r
5861     break;\r
5862   case '$':\r
5863     /* System sound from Control Panel.  Don't preload here. */\r
5864     ok = TRUE;\r
5865     break;\r
5866   case '!':\r
5867     if (ms->name[1] == NULLCHAR) {\r
5868       /* "!" alone = silence */\r
5869       ok = TRUE;\r
5870     } else {\r
5871       /* Builtin wave resource.  Error if not found. */\r
5872       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5873       if (h == NULL) break;\r
5874       ms->data = (void *)LoadResource(hInst, h);\r
5875       ms->flag = 0; // not maloced, so cannot be freed!\r
5876       if (h == NULL) break;\r
5877       ok = TRUE;\r
5878     }\r
5879     break;\r
5880   default:\r
5881     /* .wav file.  Error if not found. */\r
5882     f = fopen(ms->name, "rb");\r
5883     if (f == NULL) break;\r
5884     if (fstat(fileno(f), &st) < 0) break;\r
5885     ms->data = malloc(st.st_size);\r
5886     ms->flag = 1;\r
5887     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5888     fclose(f);\r
5889     ok = TRUE;\r
5890     break;\r
5891   }\r
5892   if (!ok) {\r
5893     char buf[MSG_SIZ];\r
5894       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5895     DisplayError(buf, GetLastError());\r
5896   }\r
5897   return ok;\r
5898 }\r
5899 \r
5900 BOOLEAN\r
5901 MyPlaySound(MySound *ms)\r
5902 {\r
5903   BOOLEAN ok = FALSE;\r
5904 \r
5905   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5906   switch (ms->name[0]) {\r
5907   case NULLCHAR:\r
5908         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5909     /* Silence */\r
5910     ok = TRUE;\r
5911     break;\r
5912   case '$':\r
5913     /* System sound from Control Panel (deprecated feature).\r
5914        "$" alone or an unset sound name gets default beep (still in use). */\r
5915     if (ms->name[1]) {\r
5916       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5917     }\r
5918     if (!ok) ok = MessageBeep(MB_OK);\r
5919     break; \r
5920   case '!':\r
5921     /* Builtin wave resource, or "!" alone for silence */\r
5922     if (ms->name[1]) {\r
5923       if (ms->data == NULL) return FALSE;\r
5924       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5925     } else {\r
5926       ok = TRUE;\r
5927     }\r
5928     break;\r
5929   default:\r
5930     /* .wav file.  Error if not found. */\r
5931     if (ms->data == NULL) return FALSE;\r
5932     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5933     break;\r
5934   }\r
5935   /* Don't print an error: this can happen innocently if the sound driver\r
5936      is busy; for instance, if another instance of WinBoard is playing\r
5937      a sound at about the same time. */\r
5938   return ok;\r
5939 }\r
5940 \r
5941 \r
5942 LRESULT CALLBACK\r
5943 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5944 {\r
5945   BOOL ok;\r
5946   OPENFILENAME *ofn;\r
5947   static UINT *number; /* gross that this is static */\r
5948 \r
5949   switch (message) {\r
5950   case WM_INITDIALOG: /* message: initialize dialog box */\r
5951     /* Center the dialog over the application window */\r
5952     ofn = (OPENFILENAME *) lParam;\r
5953     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5954       number = (UINT *) ofn->lCustData;\r
5955       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5956     } else {\r
5957       number = NULL;\r
5958     }\r
5959     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5960     Translate(hDlg, 1536);\r
5961     return FALSE;  /* Allow for further processing */\r
5962 \r
5963   case WM_COMMAND:\r
5964     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5965       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5966     }\r
5967     return FALSE;  /* Allow for further processing */\r
5968   }\r
5969   return FALSE;\r
5970 }\r
5971 \r
5972 UINT APIENTRY\r
5973 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5974 {\r
5975   static UINT *number;\r
5976   OPENFILENAME *ofname;\r
5977   OFNOTIFY *ofnot;\r
5978   switch (uiMsg) {\r
5979   case WM_INITDIALOG:\r
5980     Translate(hdlg, DLG_IndexNumber);\r
5981     ofname = (OPENFILENAME *)lParam;\r
5982     number = (UINT *)(ofname->lCustData);\r
5983     break;\r
5984   case WM_NOTIFY:\r
5985     ofnot = (OFNOTIFY *)lParam;\r
5986     if (ofnot->hdr.code == CDN_FILEOK) {\r
5987       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5988     }\r
5989     break;\r
5990   }\r
5991   return 0;\r
5992 }\r
5993 \r
5994 \r
5995 FILE *\r
5996 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5997                char *nameFilt, char *dlgTitle, UINT *number,\r
5998                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5999 {\r
6000   OPENFILENAME openFileName;\r
6001   char buf1[MSG_SIZ];\r
6002   FILE *f;\r
6003 \r
6004   if (fileName == NULL) fileName = buf1;\r
6005   if (defName == NULL) {\r
6006     safeStrCpy(fileName, "*.", 3 );\r
6007     strcat(fileName, defExt);\r
6008   } else {\r
6009     safeStrCpy(fileName, defName, MSG_SIZ );\r
6010   }\r
6011     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
6012   if (number) *number = 0;\r
6013 \r
6014   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6015   openFileName.hwndOwner         = hwnd;\r
6016   openFileName.hInstance         = (HANDLE) hInst;\r
6017   openFileName.lpstrFilter       = nameFilt;\r
6018   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6019   openFileName.nMaxCustFilter    = 0L;\r
6020   openFileName.nFilterIndex      = 1L;\r
6021   openFileName.lpstrFile         = fileName;\r
6022   openFileName.nMaxFile          = MSG_SIZ;\r
6023   openFileName.lpstrFileTitle    = fileTitle;\r
6024   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6025   openFileName.lpstrInitialDir   = NULL;\r
6026   openFileName.lpstrTitle        = dlgTitle;\r
6027   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6028     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6029     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6030     | (oldDialog ? 0 : OFN_EXPLORER);\r
6031   openFileName.nFileOffset       = 0;\r
6032   openFileName.nFileExtension    = 0;\r
6033   openFileName.lpstrDefExt       = defExt;\r
6034   openFileName.lCustData         = (LONG) number;\r
6035   openFileName.lpfnHook          = oldDialog ?\r
6036     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6037   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6038 \r
6039   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6040                         GetOpenFileName(&openFileName)) {\r
6041     /* open the file */\r
6042     f = fopen(openFileName.lpstrFile, write);\r
6043     if (f == NULL) {\r
6044       MessageBox(hwnd, _("File open failed"), NULL,\r
6045                  MB_OK|MB_ICONEXCLAMATION);\r
6046       return NULL;\r
6047     }\r
6048   } else {\r
6049     int err = CommDlgExtendedError();\r
6050     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
6051     return FALSE;\r
6052   }\r
6053   return f;\r
6054 }\r
6055 \r
6056 \r
6057 \r
6058 VOID APIENTRY\r
6059 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6060 {\r
6061   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6062 \r
6063   /*\r
6064    * Get the first pop-up menu in the menu template. This is the\r
6065    * menu that TrackPopupMenu displays.\r
6066    */\r
6067   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6068   TranslateOneMenu(10, hmenuTrackPopup);\r
6069 \r
6070   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6071 \r
6072   /*\r
6073    * TrackPopup uses screen coordinates, so convert the\r
6074    * coordinates of the mouse click to screen coordinates.\r
6075    */\r
6076   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6077 \r
6078   /* Draw and track the floating pop-up menu. */\r
6079   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6080                  pt.x, pt.y, 0, hwnd, NULL);\r
6081 \r
6082   /* Destroy the menu.*/\r
6083   DestroyMenu(hmenu);\r
6084 }\r
6085    \r
6086 typedef struct {\r
6087   HWND hDlg, hText;\r
6088   int sizeX, sizeY, newSizeX, newSizeY;\r
6089   HDWP hdwp;\r
6090 } ResizeEditPlusButtonsClosure;\r
6091 \r
6092 BOOL CALLBACK\r
6093 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6094 {\r
6095   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6096   RECT rect;\r
6097   POINT pt;\r
6098 \r
6099   if (hChild == cl->hText) return TRUE;\r
6100   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6101   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6102   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6103   ScreenToClient(cl->hDlg, &pt);\r
6104   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6105     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6106   return TRUE;\r
6107 }\r
6108 \r
6109 /* Resize a dialog that has a (rich) edit field filling most of\r
6110    the top, with a row of buttons below */\r
6111 VOID\r
6112 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6113 {\r
6114   RECT rectText;\r
6115   int newTextHeight, newTextWidth;\r
6116   ResizeEditPlusButtonsClosure cl;\r
6117   \r
6118   /*if (IsIconic(hDlg)) return;*/\r
6119   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6120   \r
6121   cl.hdwp = BeginDeferWindowPos(8);\r
6122 \r
6123   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6124   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6125   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6126   if (newTextHeight < 0) {\r
6127     newSizeY += -newTextHeight;\r
6128     newTextHeight = 0;\r
6129   }\r
6130   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6131     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6132 \r
6133   cl.hDlg = hDlg;\r
6134   cl.hText = hText;\r
6135   cl.sizeX = sizeX;\r
6136   cl.sizeY = sizeY;\r
6137   cl.newSizeX = newSizeX;\r
6138   cl.newSizeY = newSizeY;\r
6139   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6140 \r
6141   EndDeferWindowPos(cl.hdwp);\r
6142 }\r
6143 \r
6144 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6145 {\r
6146     RECT    rChild, rParent;\r
6147     int     wChild, hChild, wParent, hParent;\r
6148     int     wScreen, hScreen, xNew, yNew;\r
6149     HDC     hdc;\r
6150 \r
6151     /* Get the Height and Width of the child window */\r
6152     GetWindowRect (hwndChild, &rChild);\r
6153     wChild = rChild.right - rChild.left;\r
6154     hChild = rChild.bottom - rChild.top;\r
6155 \r
6156     /* Get the Height and Width of the parent window */\r
6157     GetWindowRect (hwndParent, &rParent);\r
6158     wParent = rParent.right - rParent.left;\r
6159     hParent = rParent.bottom - rParent.top;\r
6160 \r
6161     /* Get the display limits */\r
6162     hdc = GetDC (hwndChild);\r
6163     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6164     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6165     ReleaseDC(hwndChild, hdc);\r
6166 \r
6167     /* Calculate new X position, then adjust for screen */\r
6168     xNew = rParent.left + ((wParent - wChild) /2);\r
6169     if (xNew < 0) {\r
6170         xNew = 0;\r
6171     } else if ((xNew+wChild) > wScreen) {\r
6172         xNew = wScreen - wChild;\r
6173     }\r
6174 \r
6175     /* Calculate new Y position, then adjust for screen */\r
6176     if( mode == 0 ) {\r
6177         yNew = rParent.top  + ((hParent - hChild) /2);\r
6178     }\r
6179     else {\r
6180         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6181     }\r
6182 \r
6183     if (yNew < 0) {\r
6184         yNew = 0;\r
6185     } else if ((yNew+hChild) > hScreen) {\r
6186         yNew = hScreen - hChild;\r
6187     }\r
6188 \r
6189     /* Set it, and return */\r
6190     return SetWindowPos (hwndChild, NULL,\r
6191                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6192 }\r
6193 \r
6194 /* Center one window over another */\r
6195 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6196 {\r
6197     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6198 }\r
6199 \r
6200 /*---------------------------------------------------------------------------*\\r
6201  *\r
6202  * Startup Dialog functions\r
6203  *\r
6204 \*---------------------------------------------------------------------------*/\r
6205 void\r
6206 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6207 {\r
6208   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6209 \r
6210   while (*cd != NULL) {\r
6211     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6212     cd++;\r
6213   }\r
6214 }\r
6215 \r
6216 void\r
6217 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6218 {\r
6219   char buf1[MAX_ARG_LEN];\r
6220   int len;\r
6221 \r
6222   if (str[0] == '@') {\r
6223     FILE* f = fopen(str + 1, "r");\r
6224     if (f == NULL) {\r
6225       DisplayFatalError(str + 1, errno, 2);\r
6226       return;\r
6227     }\r
6228     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6229     fclose(f);\r
6230     buf1[len] = NULLCHAR;\r
6231     str = buf1;\r
6232   }\r
6233 \r
6234   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6235 \r
6236   for (;;) {\r
6237     char buf[MSG_SIZ];\r
6238     char *end = strchr(str, '\n');\r
6239     if (end == NULL) return;\r
6240     memcpy(buf, str, end - str);\r
6241     buf[end - str] = NULLCHAR;\r
6242     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6243     str = end + 1;\r
6244   }\r
6245 }\r
6246 \r
6247 void\r
6248 SetStartupDialogEnables(HWND hDlg)\r
6249 {\r
6250   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6251     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6252     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6253   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6254     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6255   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6256     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6257   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6258     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6259   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6260     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6261     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6262     IsDlgButtonChecked(hDlg, OPT_View));\r
6263 }\r
6264 \r
6265 char *\r
6266 QuoteForFilename(char *filename)\r
6267 {\r
6268   int dquote, space;\r
6269   dquote = strchr(filename, '"') != NULL;\r
6270   space = strchr(filename, ' ') != NULL;\r
6271   if (dquote || space) {\r
6272     if (dquote) {\r
6273       return "'";\r
6274     } else {\r
6275       return "\"";\r
6276     }\r
6277   } else {\r
6278     return "";\r
6279   }\r
6280 }\r
6281 \r
6282 VOID\r
6283 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6284 {\r
6285   char buf[MSG_SIZ];\r
6286   char *q;\r
6287 \r
6288   InitComboStringsFromOption(hwndCombo, nthnames);\r
6289   q = QuoteForFilename(nthcp);\r
6290     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6291   if (*nthdir != NULLCHAR) {\r
6292     q = QuoteForFilename(nthdir);\r
6293       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6294   }\r
6295   if (*nthcp == NULLCHAR) {\r
6296     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6297   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6298     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6299     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6300   }\r
6301 }\r
6302 \r
6303 LRESULT CALLBACK\r
6304 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6305 {\r
6306   char buf[MSG_SIZ];\r
6307   HANDLE hwndCombo;\r
6308   char *p;\r
6309 \r
6310   switch (message) {\r
6311   case WM_INITDIALOG:\r
6312     /* Center the dialog */\r
6313     CenterWindow (hDlg, GetDesktopWindow());\r
6314     Translate(hDlg, DLG_Startup);\r
6315     /* Initialize the dialog items */\r
6316     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6317                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6318                   firstChessProgramNames);\r
6319     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6320                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6321                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6322     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6323     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6324       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6325     if (*appData.icsHelper != NULLCHAR) {\r
6326       char *q = QuoteForFilename(appData.icsHelper);\r
6327       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6328     }\r
6329     if (*appData.icsHost == NULLCHAR) {\r
6330       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6331       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6332     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6333       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6334       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6335     }\r
6336 \r
6337     if (appData.icsActive) {\r
6338       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6339     }\r
6340     else if (appData.noChessProgram) {\r
6341       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6342     }\r
6343     else {\r
6344       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6345     }\r
6346 \r
6347     SetStartupDialogEnables(hDlg);\r
6348     return TRUE;\r
6349 \r
6350   case WM_COMMAND:\r
6351     switch (LOWORD(wParam)) {\r
6352     case IDOK:\r
6353       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6354         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6355         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6356         p = buf;\r
6357         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6358         ParseArgs(StringGet, &p);\r
6359         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6360         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6361         p = buf;\r
6362         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6363         ParseArgs(StringGet, &p);\r
6364         SwapEngines(singleList); // ... and then make it 'second'\r
6365 \r
6366         appData.noChessProgram = FALSE;\r
6367         appData.icsActive = FALSE;\r
6368       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6369         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6370         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6371         p = buf;\r
6372         ParseArgs(StringGet, &p);\r
6373         if (appData.zippyPlay) {\r
6374           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6375           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6376           p = buf;\r
6377           ParseArgs(StringGet, &p);\r
6378         }\r
6379       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6380         appData.noChessProgram = TRUE;\r
6381         appData.icsActive = FALSE;\r
6382       } else {\r
6383         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6384                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6385         return TRUE;\r
6386       }\r
6387       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6388         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6389         p = buf;\r
6390         ParseArgs(StringGet, &p);\r
6391       }\r
6392       EndDialog(hDlg, TRUE);\r
6393       return TRUE;\r
6394 \r
6395     case IDCANCEL:\r
6396       ExitEvent(0);\r
6397       return TRUE;\r
6398 \r
6399     case IDM_HELPCONTENTS:\r
6400       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6401         MessageBox (GetFocus(),\r
6402                     _("Unable to activate help"),\r
6403                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6404       }\r
6405       break;\r
6406 \r
6407     default:\r
6408       SetStartupDialogEnables(hDlg);\r
6409       break;\r
6410     }\r
6411     break;\r
6412   }\r
6413   return FALSE;\r
6414 }\r
6415 \r
6416 /*---------------------------------------------------------------------------*\\r
6417  *\r
6418  * About box dialog functions\r
6419  *\r
6420 \*---------------------------------------------------------------------------*/\r
6421 \r
6422 /* Process messages for "About" dialog box */\r
6423 LRESULT CALLBACK\r
6424 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6425 {\r
6426   switch (message) {\r
6427   case WM_INITDIALOG: /* message: initialize dialog box */\r
6428     /* Center the dialog over the application window */\r
6429     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6430     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6431     Translate(hDlg, ABOUTBOX);\r
6432     JAWS_COPYRIGHT\r
6433     return (TRUE);\r
6434 \r
6435   case WM_COMMAND: /* message: received a command */\r
6436     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6437         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6438       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6439       return (TRUE);\r
6440     }\r
6441     break;\r
6442   }\r
6443   return (FALSE);\r
6444 }\r
6445 \r
6446 /*---------------------------------------------------------------------------*\\r
6447  *\r
6448  * Comment Dialog functions\r
6449  *\r
6450 \*---------------------------------------------------------------------------*/\r
6451 \r
6452 LRESULT CALLBACK\r
6453 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6454 {\r
6455   static HANDLE hwndText = NULL;\r
6456   int len, newSizeX, newSizeY;\r
6457   static int sizeX, sizeY;\r
6458   char *str;\r
6459   RECT rect;\r
6460   MINMAXINFO *mmi;\r
6461 \r
6462   switch (message) {\r
6463   case WM_INITDIALOG: /* message: initialize dialog box */\r
6464     /* Initialize the dialog items */\r
6465     Translate(hDlg, DLG_EditComment);\r
6466     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6467     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6468     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6469     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6470     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6471     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6472     SetWindowText(hDlg, commentTitle);\r
6473     if (editComment) {\r
6474       SetFocus(hwndText);\r
6475     } else {\r
6476       SetFocus(GetDlgItem(hDlg, IDOK));\r
6477     }\r
6478     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6479                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6480                 MAKELPARAM(FALSE, 0));\r
6481     /* Size and position the dialog */\r
6482     if (!commentDialog) {\r
6483       commentDialog = hDlg;\r
6484       GetClientRect(hDlg, &rect);\r
6485       sizeX = rect.right;\r
6486       sizeY = rect.bottom;\r
6487       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6488           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6489         WINDOWPLACEMENT wp;\r
6490         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6491         wp.length = sizeof(WINDOWPLACEMENT);\r
6492         wp.flags = 0;\r
6493         wp.showCmd = SW_SHOW;\r
6494         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6495         wp.rcNormalPosition.left = wpComment.x;\r
6496         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6497         wp.rcNormalPosition.top = wpComment.y;\r
6498         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6499         SetWindowPlacement(hDlg, &wp);\r
6500 \r
6501         GetClientRect(hDlg, &rect);\r
6502         newSizeX = rect.right;\r
6503         newSizeY = rect.bottom;\r
6504         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6505                               newSizeX, newSizeY);\r
6506         sizeX = newSizeX;\r
6507         sizeY = newSizeY;\r
6508       }\r
6509     }\r
6510     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6511     return FALSE;\r
6512 \r
6513   case WM_COMMAND: /* message: received a command */\r
6514     switch (LOWORD(wParam)) {\r
6515     case IDOK:\r
6516       if (editComment) {\r
6517         char *p, *q;\r
6518         /* Read changed options from the dialog box */\r
6519         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6520         len = GetWindowTextLength(hwndText);\r
6521         str = (char *) malloc(len + 1);\r
6522         GetWindowText(hwndText, str, len + 1);\r
6523         p = q = str;\r
6524         while (*q) {\r
6525           if (*q == '\r')\r
6526             q++;\r
6527           else\r
6528             *p++ = *q++;\r
6529         }\r
6530         *p = NULLCHAR;\r
6531         ReplaceComment(commentIndex, str);\r
6532         free(str);\r
6533       }\r
6534       CommentPopDown();\r
6535       return TRUE;\r
6536 \r
6537     case IDCANCEL:\r
6538     case OPT_CancelComment:\r
6539       CommentPopDown();\r
6540       return TRUE;\r
6541 \r
6542     case OPT_ClearComment:\r
6543       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6544       break;\r
6545 \r
6546     case OPT_EditComment:\r
6547       EditCommentEvent();\r
6548       return TRUE;\r
6549 \r
6550     default:\r
6551       break;\r
6552     }\r
6553     break;\r
6554 \r
6555   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6556         if( wParam == OPT_CommentText ) {\r
6557             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6558 \r
6559             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6560                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6561                 POINTL pt;\r
6562                 LRESULT index;\r
6563 \r
6564                 pt.x = LOWORD( lpMF->lParam );\r
6565                 pt.y = HIWORD( lpMF->lParam );\r
6566 \r
6567                 if(lpMF->msg == WM_CHAR) {\r
6568                         CHARRANGE sel;\r
6569                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6570                         index = sel.cpMin;\r
6571                 } else\r
6572                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6573 \r
6574                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6575                 len = GetWindowTextLength(hwndText);\r
6576                 str = (char *) malloc(len + 1);\r
6577                 GetWindowText(hwndText, str, len + 1);\r
6578                 ReplaceComment(commentIndex, str);\r
6579                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6580                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6581                 free(str);\r
6582 \r
6583                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6584                 lpMF->msg = WM_USER;\r
6585 \r
6586                 return TRUE;\r
6587             }\r
6588         }\r
6589         break;\r
6590 \r
6591   case WM_SIZE:\r
6592     newSizeX = LOWORD(lParam);\r
6593     newSizeY = HIWORD(lParam);\r
6594     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6595     sizeX = newSizeX;\r
6596     sizeY = newSizeY;\r
6597     break;\r
6598 \r
6599   case WM_GETMINMAXINFO:\r
6600     /* Prevent resizing window too small */\r
6601     mmi = (MINMAXINFO *) lParam;\r
6602     mmi->ptMinTrackSize.x = 100;\r
6603     mmi->ptMinTrackSize.y = 100;\r
6604     break;\r
6605   }\r
6606   return FALSE;\r
6607 }\r
6608 \r
6609 VOID\r
6610 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6611 {\r
6612   FARPROC lpProc;\r
6613   char *p, *q;\r
6614 \r
6615   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6616 \r
6617   if (str == NULL) str = "";\r
6618   p = (char *) malloc(2 * strlen(str) + 2);\r
6619   q = p;\r
6620   while (*str) {\r
6621     if (*str == '\n') *q++ = '\r';\r
6622     *q++ = *str++;\r
6623   }\r
6624   *q = NULLCHAR;\r
6625   if (commentText != NULL) free(commentText);\r
6626 \r
6627   commentIndex = index;\r
6628   commentTitle = title;\r
6629   commentText = p;\r
6630   editComment = edit;\r
6631 \r
6632   if (commentDialog) {\r
6633     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6634     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6635   } else {\r
6636     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6637     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6638                  hwndMain, (DLGPROC)lpProc);\r
6639     FreeProcInstance(lpProc);\r
6640   }\r
6641   commentUp = TRUE;\r
6642 }\r
6643 \r
6644 \r
6645 /*---------------------------------------------------------------------------*\\r
6646  *\r
6647  * Type-in move dialog functions\r
6648  * \r
6649 \*---------------------------------------------------------------------------*/\r
6650 \r
6651 LRESULT CALLBACK\r
6652 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6653 {\r
6654   char move[MSG_SIZ];\r
6655   HWND hInput;\r
6656 \r
6657   switch (message) {\r
6658   case WM_INITDIALOG:\r
6659     move[0] = (char) lParam;\r
6660     move[1] = NULLCHAR;\r
6661     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6662     Translate(hDlg, DLG_TypeInMove);\r
6663     hInput = GetDlgItem(hDlg, OPT_Move);\r
6664     SetWindowText(hInput, move);\r
6665     SetFocus(hInput);\r
6666     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6667     return FALSE;\r
6668 \r
6669   case WM_COMMAND:\r
6670     switch (LOWORD(wParam)) {\r
6671     case IDOK:\r
6672 \r
6673       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6674       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6675       TypeInDoneEvent(move);\r
6676       EndDialog(hDlg, TRUE);\r
6677       return TRUE;\r
6678     case IDCANCEL:\r
6679       EndDialog(hDlg, FALSE);\r
6680       return TRUE;\r
6681     default:\r
6682       break;\r
6683     }\r
6684     break;\r
6685   }\r
6686   return FALSE;\r
6687 }\r
6688 \r
6689 VOID\r
6690 PopUpMoveDialog(char firstchar)\r
6691 {\r
6692     FARPROC lpProc;\r
6693 \r
6694       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6695       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6696         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6697       FreeProcInstance(lpProc);\r
6698 }\r
6699 \r
6700 /*---------------------------------------------------------------------------*\\r
6701  *\r
6702  * Type-in name dialog functions\r
6703  * \r
6704 \*---------------------------------------------------------------------------*/\r
6705 \r
6706 LRESULT CALLBACK\r
6707 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6708 {\r
6709   char move[MSG_SIZ];\r
6710   HWND hInput;\r
6711 \r
6712   switch (message) {\r
6713   case WM_INITDIALOG:\r
6714     move[0] = (char) lParam;\r
6715     move[1] = NULLCHAR;\r
6716     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6717     Translate(hDlg, DLG_TypeInName);\r
6718     hInput = GetDlgItem(hDlg, OPT_Name);\r
6719     SetWindowText(hInput, move);\r
6720     SetFocus(hInput);\r
6721     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6722     return FALSE;\r
6723 \r
6724   case WM_COMMAND:\r
6725     switch (LOWORD(wParam)) {\r
6726     case IDOK:\r
6727       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6728       appData.userName = strdup(move);\r
6729       SetUserLogo(); DisplayLogos();\r
6730       SetGameInfo();\r
6731       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6732         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6733         DisplayTitle(move);\r
6734       }\r
6735 \r
6736 \r
6737       EndDialog(hDlg, TRUE);\r
6738       return TRUE;\r
6739     case IDCANCEL:\r
6740       EndDialog(hDlg, FALSE);\r
6741       return TRUE;\r
6742     default:\r
6743       break;\r
6744     }\r
6745     break;\r
6746   }\r
6747   return FALSE;\r
6748 }\r
6749 \r
6750 VOID\r
6751 PopUpNameDialog(char firstchar)\r
6752 {\r
6753     FARPROC lpProc;\r
6754     \r
6755       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6756       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6757         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6758       FreeProcInstance(lpProc);\r
6759 }\r
6760 \r
6761 /*---------------------------------------------------------------------------*\\r
6762  *\r
6763  *  Error dialogs\r
6764  * \r
6765 \*---------------------------------------------------------------------------*/\r
6766 \r
6767 /* Nonmodal error box */\r
6768 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6769                              WPARAM wParam, LPARAM lParam);\r
6770 \r
6771 VOID\r
6772 ErrorPopUp(char *title, char *content)\r
6773 {\r
6774   FARPROC lpProc;\r
6775   char *p, *q;\r
6776   BOOLEAN modal = hwndMain == NULL;\r
6777 \r
6778   p = content;\r
6779   q = errorMessage;\r
6780   while (*p) {\r
6781     if (*p == '\n') {\r
6782       if (modal) {\r
6783         *q++ = ' ';\r
6784         p++;\r
6785       } else {\r
6786         *q++ = '\r';\r
6787         *q++ = *p++;\r
6788       }\r
6789     } else {\r
6790       *q++ = *p++;\r
6791     }\r
6792   }\r
6793   *q = NULLCHAR;\r
6794   strncpy(errorTitle, title, sizeof(errorTitle));\r
6795   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6796   \r
6797   if (modal) {\r
6798     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6799   } else {\r
6800     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6801     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6802                  hwndMain, (DLGPROC)lpProc);\r
6803     FreeProcInstance(lpProc);\r
6804   }\r
6805 }\r
6806 \r
6807 VOID\r
6808 ErrorPopDown()\r
6809 {\r
6810   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6811   if (errorDialog == NULL) return;\r
6812   DestroyWindow(errorDialog);\r
6813   errorDialog = NULL;\r
6814   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6815 }\r
6816 \r
6817 LRESULT CALLBACK\r
6818 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6819 {\r
6820   RECT rChild;\r
6821 \r
6822   switch (message) {\r
6823   case WM_INITDIALOG:\r
6824     GetWindowRect(hDlg, &rChild);\r
6825 \r
6826     /*\r
6827     SetWindowPos(hDlg, NULL, rChild.left,\r
6828       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6829       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6830     */\r
6831 \r
6832     /* \r
6833         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6834         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6835         and it doesn't work when you resize the dialog.\r
6836         For now, just give it a default position.\r
6837     */\r
6838     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6839     Translate(hDlg, DLG_Error);\r
6840 \r
6841     errorDialog = hDlg;\r
6842     SetWindowText(hDlg, errorTitle);\r
6843     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6844     return FALSE;\r
6845 \r
6846   case WM_COMMAND:\r
6847     switch (LOWORD(wParam)) {\r
6848     case IDOK:\r
6849     case IDCANCEL:\r
6850       if (errorDialog == hDlg) errorDialog = NULL;\r
6851       DestroyWindow(hDlg);\r
6852       return TRUE;\r
6853 \r
6854     default:\r
6855       break;\r
6856     }\r
6857     break;\r
6858   }\r
6859   return FALSE;\r
6860 }\r
6861 \r
6862 #ifdef GOTHIC\r
6863 HWND gothicDialog = NULL;\r
6864 \r
6865 LRESULT CALLBACK\r
6866 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6867 {\r
6868   RECT rChild;\r
6869   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6870 \r
6871   switch (message) {\r
6872   case WM_INITDIALOG:\r
6873     GetWindowRect(hDlg, &rChild);\r
6874 \r
6875     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6876                                                              SWP_NOZORDER);\r
6877 \r
6878     /* \r
6879         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6880         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6881         and it doesn't work when you resize the dialog.\r
6882         For now, just give it a default position.\r
6883     */\r
6884     gothicDialog = 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 VOID\r
6906 GothicPopUp(char *title, VariantClass variant)\r
6907 {\r
6908   FARPROC lpProc;\r
6909   static char *lastTitle;\r
6910 \r
6911   strncpy(errorTitle, title, sizeof(errorTitle));\r
6912   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6913 \r
6914   if(lastTitle != title && gothicDialog != NULL) {\r
6915     DestroyWindow(gothicDialog);\r
6916     gothicDialog = NULL;\r
6917   }\r
6918   if(variant != VariantNormal && gothicDialog == NULL) {\r
6919     title = lastTitle;\r
6920     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6921     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6922                  hwndMain, (DLGPROC)lpProc);\r
6923     FreeProcInstance(lpProc);\r
6924   }\r
6925 }\r
6926 #endif\r
6927 \r
6928 /*---------------------------------------------------------------------------*\\r
6929  *\r
6930  *  Ics Interaction console functions\r
6931  *\r
6932 \*---------------------------------------------------------------------------*/\r
6933 \r
6934 #define HISTORY_SIZE 64\r
6935 static char *history[HISTORY_SIZE];\r
6936 int histIn = 0, histP = 0;\r
6937 \r
6938 \r
6939 VOID\r
6940 SaveInHistory(char *cmd)\r
6941 {\r
6942   if (history[histIn] != NULL) {\r
6943     free(history[histIn]);\r
6944     history[histIn] = NULL;\r
6945   }\r
6946   if (*cmd == NULLCHAR) return;\r
6947   history[histIn] = StrSave(cmd);\r
6948   histIn = (histIn + 1) % HISTORY_SIZE;\r
6949   if (history[histIn] != NULL) {\r
6950     free(history[histIn]);\r
6951 \r
6952     history[histIn] = NULL;\r
6953   }\r
6954   histP = histIn;\r
6955 }\r
6956 \r
6957 char *\r
6958 PrevInHistory(char *cmd)\r
6959 {\r
6960   int newhp;\r
6961   if (histP == histIn) {\r
6962     if (history[histIn] != NULL) free(history[histIn]);\r
6963     history[histIn] = StrSave(cmd);\r
6964   }\r
6965   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6966   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6967   histP = newhp;\r
6968   return history[histP];\r
6969 }\r
6970 \r
6971 char *\r
6972 NextInHistory()\r
6973 {\r
6974   if (histP == histIn) return NULL;\r
6975   histP = (histP + 1) % HISTORY_SIZE;\r
6976   return history[histP];   \r
6977 }\r
6978 \r
6979 HMENU\r
6980 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6981 {\r
6982   HMENU hmenu, h;\r
6983   int i = 0;\r
6984   hmenu = LoadMenu(hInst, "TextMenu");\r
6985   h = GetSubMenu(hmenu, 0);\r
6986   while (e->item) {\r
6987     if (strcmp(e->item, "-") == 0) {\r
6988       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6989     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6990       int flags = MF_STRING, j = 0;\r
6991       if (e->item[0] == '|') {\r
6992         flags |= MF_MENUBARBREAK;\r
6993         j++;\r
6994       }\r
6995       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6996       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6997     }\r
6998     e++;\r
6999     i++;\r
7000   } \r
7001   return hmenu;\r
7002 }\r
7003 \r
7004 WNDPROC consoleTextWindowProc;\r
7005 \r
7006 void\r
7007 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7008 {\r
7009   char buf[MSG_SIZ], name[MSG_SIZ];\r
7010   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7011   CHARRANGE sel;\r
7012 \r
7013   if (!getname) {\r
7014     SetWindowText(hInput, command);\r
7015     if (immediate) {\r
7016       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7017     } else {\r
7018       sel.cpMin = 999999;\r
7019       sel.cpMax = 999999;\r
7020       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7021       SetFocus(hInput);\r
7022     }\r
7023     return;\r
7024   }    \r
7025   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7026   if (sel.cpMin == sel.cpMax) {\r
7027     /* Expand to surrounding word */\r
7028     TEXTRANGE tr;\r
7029     do {\r
7030       tr.chrg.cpMax = sel.cpMin;\r
7031       tr.chrg.cpMin = --sel.cpMin;\r
7032       if (sel.cpMin < 0) break;\r
7033       tr.lpstrText = name;\r
7034       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7035     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7036     sel.cpMin++;\r
7037 \r
7038     do {\r
7039       tr.chrg.cpMin = sel.cpMax;\r
7040       tr.chrg.cpMax = ++sel.cpMax;\r
7041       tr.lpstrText = name;\r
7042       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7043     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7044     sel.cpMax--;\r
7045 \r
7046     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7047       MessageBeep(MB_ICONEXCLAMATION);\r
7048       return;\r
7049     }\r
7050     tr.chrg = sel;\r
7051     tr.lpstrText = name;\r
7052     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7053   } else {\r
7054     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7055       MessageBeep(MB_ICONEXCLAMATION);\r
7056       return;\r
7057     }\r
7058     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7059   }\r
7060   if (immediate) {\r
7061     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
7062     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
7063     SetWindowText(hInput, buf);\r
7064     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7065   } else {\r
7066     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
7067       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
7068     SetWindowText(hInput, buf);\r
7069     sel.cpMin = 999999;\r
7070     sel.cpMax = 999999;\r
7071     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7072     SetFocus(hInput);\r
7073   }\r
7074 }\r
7075 \r
7076 LRESULT CALLBACK \r
7077 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7078 {\r
7079   HWND hInput;\r
7080   CHARRANGE sel;\r
7081 \r
7082   switch (message) {\r
7083   case WM_KEYDOWN:\r
7084     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7085     if(wParam=='R') return 0;\r
7086     switch (wParam) {\r
7087     case VK_PRIOR:\r
7088       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7089       return 0;\r
7090     case VK_NEXT:\r
7091       sel.cpMin = 999999;\r
7092       sel.cpMax = 999999;\r
7093       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7094       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7095       return 0;\r
7096     }\r
7097     break;\r
7098   case WM_CHAR:\r
7099    if(wParam != '\022') {\r
7100     if (wParam == '\t') {\r
7101       if (GetKeyState(VK_SHIFT) < 0) {\r
7102         /* shifted */\r
7103         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7104         if (buttonDesc[0].hwnd) {\r
7105           SetFocus(buttonDesc[0].hwnd);\r
7106         } else {\r
7107           SetFocus(hwndMain);\r
7108         }\r
7109       } else {\r
7110         /* unshifted */\r
7111         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7112       }\r
7113     } else {\r
7114       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7115       JAWS_DELETE( SetFocus(hInput); )\r
7116       SendMessage(hInput, message, wParam, lParam);\r
7117     }\r
7118     return 0;\r
7119    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
7120    lParam = -1;\r
7121   case WM_RBUTTONDOWN:\r
7122     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7123       /* Move selection here if it was empty */\r
7124       POINT pt;\r
7125       pt.x = LOWORD(lParam);\r
7126       pt.y = HIWORD(lParam);\r
7127       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7128       if (sel.cpMin == sel.cpMax) {\r
7129         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7130         sel.cpMax = sel.cpMin;\r
7131         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7132       }\r
7133       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7134 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
7135       POINT pt;\r
7136       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7137       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7138       if (sel.cpMin == sel.cpMax) {\r
7139         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7140         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7141       }\r
7142       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7143         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7144       }\r
7145       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
7146       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
7147       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
7148       MenuPopup(hwnd, pt, hmenu, -1);\r
7149 }\r
7150     }\r
7151     return 0;\r
7152   case WM_RBUTTONUP:\r
7153     if (GetKeyState(VK_SHIFT) & ~1) {\r
7154       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7155         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7156     }\r
7157     return 0;\r
7158   case WM_PASTE:\r
7159     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7160     SetFocus(hInput);\r
7161     return SendMessage(hInput, message, wParam, lParam);\r
7162   case WM_MBUTTONDOWN:\r
7163     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7164   case WM_COMMAND:\r
7165     switch (LOWORD(wParam)) {\r
7166     case IDM_QuickPaste:\r
7167       {\r
7168         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7169         if (sel.cpMin == sel.cpMax) {\r
7170           MessageBeep(MB_ICONEXCLAMATION);\r
7171           return 0;\r
7172         }\r
7173         SendMessage(hwnd, WM_COPY, 0, 0);\r
7174         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7175         SendMessage(hInput, WM_PASTE, 0, 0);\r
7176         SetFocus(hInput);\r
7177         return 0;\r
7178       }\r
7179     case IDM_Cut:\r
7180       SendMessage(hwnd, WM_CUT, 0, 0);\r
7181       return 0;\r
7182     case IDM_Paste:\r
7183       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7184       return 0;\r
7185     case IDM_Copy:\r
7186       SendMessage(hwnd, WM_COPY, 0, 0);\r
7187       return 0;\r
7188     default:\r
7189       {\r
7190         int i = LOWORD(wParam) - IDM_CommandX;\r
7191         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7192             icsTextMenuEntry[i].command != NULL) {\r
7193           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7194                    icsTextMenuEntry[i].getname,\r
7195                    icsTextMenuEntry[i].immediate);\r
7196           return 0;\r
7197         }\r
7198       }\r
7199       break;\r
7200     }\r
7201     break;\r
7202   }\r
7203   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7204 }\r
7205 \r
7206 WNDPROC consoleInputWindowProc;\r
7207 \r
7208 LRESULT CALLBACK\r
7209 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7210 {\r
7211   char buf[MSG_SIZ];\r
7212   char *p;\r
7213   static BOOL sendNextChar = FALSE;\r
7214   static BOOL quoteNextChar = FALSE;\r
7215   InputSource *is = consoleInputSource;\r
7216   CHARFORMAT cf;\r
7217   CHARRANGE sel;\r
7218 \r
7219   switch (message) {\r
7220   case WM_CHAR:\r
7221     if (!appData.localLineEditing || sendNextChar) {\r
7222       is->buf[0] = (CHAR) wParam;\r
7223       is->count = 1;\r
7224       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7225       sendNextChar = FALSE;\r
7226       return 0;\r
7227     }\r
7228     if (quoteNextChar) {\r
7229       buf[0] = (char) wParam;\r
7230       buf[1] = NULLCHAR;\r
7231       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7232       quoteNextChar = FALSE;\r
7233       return 0;\r
7234     }\r
7235     switch (wParam) {\r
7236     case '\r':   /* Enter key */\r
7237       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7238       if (consoleEcho) SaveInHistory(is->buf);\r
7239       is->buf[is->count++] = '\n';\r
7240       is->buf[is->count] = NULLCHAR;\r
7241       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7242       if (consoleEcho) {\r
7243         ConsoleOutput(is->buf, is->count, TRUE);\r
7244       } else if (appData.localLineEditing) {\r
7245         ConsoleOutput("\n", 1, TRUE);\r
7246       }\r
7247       /* fall thru */\r
7248     case '\033': /* Escape key */\r
7249       SetWindowText(hwnd, "");\r
7250       cf.cbSize = sizeof(CHARFORMAT);\r
7251       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7252       if (consoleEcho) {\r
7253         cf.crTextColor = textAttribs[ColorNormal].color;\r
7254       } else {\r
7255         cf.crTextColor = COLOR_ECHOOFF;\r
7256       }\r
7257       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7258       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7259       return 0;\r
7260     case '\t':   /* Tab key */\r
7261       if (GetKeyState(VK_SHIFT) < 0) {\r
7262         /* shifted */\r
7263         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7264       } else {\r
7265         /* unshifted */\r
7266         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7267         if (buttonDesc[0].hwnd) {\r
7268           SetFocus(buttonDesc[0].hwnd);\r
7269         } else {\r
7270           SetFocus(hwndMain);\r
7271         }\r
7272       }\r
7273       return 0;\r
7274     case '\023': /* Ctrl+S */\r
7275       sendNextChar = TRUE;\r
7276       return 0;\r
7277     case '\021': /* Ctrl+Q */\r
7278       quoteNextChar = TRUE;\r
7279       return 0;\r
7280     JAWS_REPLAY\r
7281     default:\r
7282       break;\r
7283     }\r
7284     break;\r
7285   case WM_KEYDOWN:\r
7286     switch (wParam) {\r
7287     case VK_UP:\r
7288       GetWindowText(hwnd, buf, MSG_SIZ);\r
7289       p = PrevInHistory(buf);\r
7290       if (p != NULL) {\r
7291         SetWindowText(hwnd, p);\r
7292         sel.cpMin = 999999;\r
7293         sel.cpMax = 999999;\r
7294         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7295         return 0;\r
7296       }\r
7297       break;\r
7298     case VK_DOWN:\r
7299       p = NextInHistory();\r
7300       if (p != NULL) {\r
7301         SetWindowText(hwnd, p);\r
7302         sel.cpMin = 999999;\r
7303         sel.cpMax = 999999;\r
7304         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7305         return 0;\r
7306       }\r
7307       break;\r
7308     case VK_HOME:\r
7309     case VK_END:\r
7310       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7311       /* fall thru */\r
7312     case VK_PRIOR:\r
7313     case VK_NEXT:\r
7314       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7315       return 0;\r
7316     }\r
7317     break;\r
7318   case WM_MBUTTONDOWN:\r
7319     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7320       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7321     break;\r
7322   case WM_RBUTTONUP:\r
7323     if (GetKeyState(VK_SHIFT) & ~1) {\r
7324       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7325         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7326     } else {\r
7327       POINT pt;\r
7328       HMENU hmenu;\r
7329       hmenu = LoadMenu(hInst, "InputMenu");\r
7330       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7331       if (sel.cpMin == sel.cpMax) {\r
7332         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7333         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7334       }\r
7335       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7336         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7337       }\r
7338       pt.x = LOWORD(lParam);\r
7339       pt.y = HIWORD(lParam);\r
7340       MenuPopup(hwnd, pt, hmenu, -1);\r
7341     }\r
7342     return 0;\r
7343   case WM_COMMAND:\r
7344     switch (LOWORD(wParam)) { \r
7345     case IDM_Undo:\r
7346       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7347       return 0;\r
7348     case IDM_SelectAll:\r
7349       sel.cpMin = 0;\r
7350       sel.cpMax = -1; /*999999?*/\r
7351       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7352       return 0;\r
7353     case IDM_Cut:\r
7354       SendMessage(hwnd, WM_CUT, 0, 0);\r
7355       return 0;\r
7356     case IDM_Paste:\r
7357       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7358       return 0;\r
7359     case IDM_Copy:\r
7360       SendMessage(hwnd, WM_COPY, 0, 0);\r
7361       return 0;\r
7362     }\r
7363     break;\r
7364   }\r
7365   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7366 }\r
7367 \r
7368 #define CO_MAX  100000\r
7369 #define CO_TRIM   1000\r
7370 \r
7371 LRESULT CALLBACK\r
7372 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7373 {\r
7374   static SnapData sd;\r
7375   HWND hText, hInput;\r
7376   RECT rect;\r
7377   static int sizeX, sizeY;\r
7378   int newSizeX, newSizeY;\r
7379   MINMAXINFO *mmi;\r
7380   WORD wMask;\r
7381 \r
7382   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7383   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7384 \r
7385   switch (message) {\r
7386   case WM_NOTIFY:\r
7387     if (((NMHDR*)lParam)->code == EN_LINK)\r
7388     {\r
7389       ENLINK *pLink = (ENLINK*)lParam;\r
7390       if (pLink->msg == WM_LBUTTONUP)\r
7391       {\r
7392         TEXTRANGE tr;\r
7393 \r
7394         tr.chrg = pLink->chrg;\r
7395         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7396         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7397         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7398         free(tr.lpstrText);\r
7399       }\r
7400     }\r
7401     break;\r
7402   case WM_INITDIALOG: /* message: initialize dialog box */\r
7403     hwndConsole = hDlg;\r
7404     SetFocus(hInput);\r
7405     consoleTextWindowProc = (WNDPROC)\r
7406       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7407     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7408     consoleInputWindowProc = (WNDPROC)\r
7409       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7410     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7411     Colorize(ColorNormal, TRUE);\r
7412     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7413     ChangedConsoleFont();\r
7414     GetClientRect(hDlg, &rect);\r
7415     sizeX = rect.right;\r
7416     sizeY = rect.bottom;\r
7417     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7418         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7419       WINDOWPLACEMENT wp;\r
7420       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7421       wp.length = sizeof(WINDOWPLACEMENT);\r
7422       wp.flags = 0;\r
7423       wp.showCmd = SW_SHOW;\r
7424       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7425       wp.rcNormalPosition.left = wpConsole.x;\r
7426       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7427       wp.rcNormalPosition.top = wpConsole.y;\r
7428       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7429       SetWindowPlacement(hDlg, &wp);\r
7430     }\r
7431 \r
7432    // [HGM] Chessknight's change 2004-07-13\r
7433    else { /* Determine Defaults */\r
7434        WINDOWPLACEMENT wp;\r
7435        wpConsole.x = wpMain.width + 1;\r
7436        wpConsole.y = wpMain.y;\r
7437        wpConsole.width = screenWidth -  wpMain.width;\r
7438        wpConsole.height = wpMain.height;\r
7439        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7440        wp.length = sizeof(WINDOWPLACEMENT);\r
7441        wp.flags = 0;\r
7442        wp.showCmd = SW_SHOW;\r
7443        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7444        wp.rcNormalPosition.left = wpConsole.x;\r
7445        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7446        wp.rcNormalPosition.top = wpConsole.y;\r
7447        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7448        SetWindowPlacement(hDlg, &wp);\r
7449     }\r
7450 \r
7451    // Allow hText to highlight URLs and send notifications on them\r
7452    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7453    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7454    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7455    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7456 \r
7457     return FALSE;\r
7458 \r
7459   case WM_SETFOCUS:\r
7460     SetFocus(hInput);\r
7461     return 0;\r
7462 \r
7463   case WM_CLOSE:\r
7464     ExitEvent(0);\r
7465     /* not reached */\r
7466     break;\r
7467 \r
7468   case WM_SIZE:\r
7469     if (IsIconic(hDlg)) break;\r
7470     newSizeX = LOWORD(lParam);\r
7471     newSizeY = HIWORD(lParam);\r
7472     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7473       RECT rectText, rectInput;\r
7474       POINT pt;\r
7475       int newTextHeight, newTextWidth;\r
7476       GetWindowRect(hText, &rectText);\r
7477       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7478       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7479       if (newTextHeight < 0) {\r
7480         newSizeY += -newTextHeight;\r
7481         newTextHeight = 0;\r
7482       }\r
7483       SetWindowPos(hText, NULL, 0, 0,\r
7484         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7485       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7486       pt.x = rectInput.left;\r
7487       pt.y = rectInput.top + newSizeY - sizeY;\r
7488       ScreenToClient(hDlg, &pt);\r
7489       SetWindowPos(hInput, NULL, \r
7490         pt.x, pt.y, /* needs client coords */   \r
7491         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7492         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7493     }\r
7494     sizeX = newSizeX;\r
7495     sizeY = newSizeY;\r
7496     break;\r
7497 \r
7498   case WM_GETMINMAXINFO:\r
7499     /* Prevent resizing window too small */\r
7500     mmi = (MINMAXINFO *) lParam;\r
7501     mmi->ptMinTrackSize.x = 100;\r
7502     mmi->ptMinTrackSize.y = 100;\r
7503     break;\r
7504 \r
7505   /* [AS] Snapping */\r
7506   case WM_ENTERSIZEMOVE:\r
7507     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7508 \r
7509   case WM_SIZING:\r
7510     return OnSizing( &sd, hDlg, wParam, lParam );\r
7511 \r
7512   case WM_MOVING:\r
7513     return OnMoving( &sd, hDlg, wParam, lParam );\r
7514 \r
7515   case WM_EXITSIZEMOVE:\r
7516         UpdateICSWidth(hText);\r
7517     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7518   }\r
7519 \r
7520   return DefWindowProc(hDlg, message, wParam, lParam);\r
7521 }\r
7522 \r
7523 \r
7524 VOID\r
7525 ConsoleCreate()\r
7526 {\r
7527   HWND hCons;\r
7528   if (hwndConsole) return;\r
7529   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7530   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7531 }\r
7532 \r
7533 \r
7534 VOID\r
7535 ConsoleOutput(char* data, int length, int forceVisible)\r
7536 {\r
7537   HWND hText;\r
7538   int trim, exlen;\r
7539   char *p, *q;\r
7540   char buf[CO_MAX+1];\r
7541   POINT pEnd;\r
7542   RECT rect;\r
7543   static int delayLF = 0;\r
7544   CHARRANGE savesel, sel;\r
7545 \r
7546   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7547   p = data;\r
7548   q = buf;\r
7549   if (delayLF) {\r
7550     *q++ = '\r';\r
7551     *q++ = '\n';\r
7552     delayLF = 0;\r
7553   }\r
7554   while (length--) {\r
7555     if (*p == '\n') {\r
7556       if (*++p) {\r
7557         *q++ = '\r';\r
7558         *q++ = '\n';\r
7559       } else {\r
7560         delayLF = 1;\r
7561       }\r
7562     } else if (*p == '\007') {\r
7563        MyPlaySound(&sounds[(int)SoundBell]);\r
7564        p++;\r
7565     } else {\r
7566       *q++ = *p++;\r
7567     }\r
7568   }\r
7569   *q = NULLCHAR;\r
7570   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7571   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7572   /* Save current selection */\r
7573   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7574   exlen = GetWindowTextLength(hText);\r
7575   /* Find out whether current end of text is visible */\r
7576   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7577   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7578   /* Trim existing text if it's too long */\r
7579   if (exlen + (q - buf) > CO_MAX) {\r
7580     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7581     sel.cpMin = 0;\r
7582     sel.cpMax = trim;\r
7583     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7584     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7585     exlen -= trim;\r
7586     savesel.cpMin -= trim;\r
7587     savesel.cpMax -= trim;\r
7588     if (exlen < 0) exlen = 0;\r
7589     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7590     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7591   }\r
7592   /* Append the new text */\r
7593   sel.cpMin = exlen;\r
7594   sel.cpMax = exlen;\r
7595   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7596   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7597   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7598   if (forceVisible || exlen == 0 ||\r
7599       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7600        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7601     /* Scroll to make new end of text visible if old end of text\r
7602        was visible or new text is an echo of user typein */\r
7603     sel.cpMin = 9999999;\r
7604     sel.cpMax = 9999999;\r
7605     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7606     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7607     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7608     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7609   }\r
7610   if (savesel.cpMax == exlen || forceVisible) {\r
7611     /* Move insert point to new end of text if it was at the old\r
7612        end of text or if the new text is an echo of user typein */\r
7613     sel.cpMin = 9999999;\r
7614     sel.cpMax = 9999999;\r
7615     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7616   } else {\r
7617     /* Restore previous selection */\r
7618     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7619   }\r
7620   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7621 }\r
7622 \r
7623 /*---------*/\r
7624 \r
7625 \r
7626 void\r
7627 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7628 {\r
7629   char buf[100];\r
7630   char *str;\r
7631   COLORREF oldFg, oldBg;\r
7632   HFONT oldFont;\r
7633   RECT rect;\r
7634 \r
7635   if(copyNumber > 1)\r
7636     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7637 \r
7638   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7639   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7640   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7641 \r
7642   rect.left = x;\r
7643   rect.right = x + squareSize;\r
7644   rect.top  = y;\r
7645   rect.bottom = y + squareSize;\r
7646   str = buf;\r
7647 \r
7648   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7649                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7650              y, ETO_CLIPPED|ETO_OPAQUE,\r
7651              &rect, str, strlen(str), NULL);\r
7652 \r
7653   (void) SetTextColor(hdc, oldFg);\r
7654   (void) SetBkColor(hdc, oldBg);\r
7655   (void) SelectObject(hdc, oldFont);\r
7656 }\r
7657 \r
7658 void\r
7659 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7660               RECT *rect, char *color, char *flagFell)\r
7661 {\r
7662   char buf[100];\r
7663   char *str;\r
7664   COLORREF oldFg, oldBg;\r
7665   HFONT oldFont;\r
7666 \r
7667   if (twoBoards && partnerUp) return;\r
7668   if (appData.clockMode) {\r
7669     if (tinyLayout == 2)\r
7670       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7671     else\r
7672       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7673     str = buf;\r
7674   } else {\r
7675     str = color;\r
7676   }\r
7677 \r
7678   if (highlight) {\r
7679     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7680     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7681   } else {\r
7682     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7683     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7684   }\r
7685 \r
7686   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7687 \r
7688   JAWS_SILENCE\r
7689 \r
7690   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7691              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7692              rect, str, strlen(str), NULL);\r
7693   if(logoHeight > 0 && appData.clockMode) {\r
7694       RECT r;\r
7695       str += strlen(color)+2;\r
7696       r.top = rect->top + logoHeight/2;\r
7697       r.left = rect->left;\r
7698       r.right = rect->right;\r
7699       r.bottom = rect->bottom;\r
7700       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7701                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7702                  &r, str, strlen(str), NULL);\r
7703   }\r
7704   (void) SetTextColor(hdc, oldFg);\r
7705   (void) SetBkColor(hdc, oldBg);\r
7706   (void) SelectObject(hdc, oldFont);\r
7707 }\r
7708 \r
7709 \r
7710 int\r
7711 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7712            OVERLAPPED *ovl)\r
7713 {\r
7714   int ok, err;\r
7715 \r
7716   /* [AS]  */\r
7717   if( count <= 0 ) {\r
7718     if (appData.debugMode) {\r
7719       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7720     }\r
7721 \r
7722     return ERROR_INVALID_USER_BUFFER;\r
7723   }\r
7724 \r
7725   ResetEvent(ovl->hEvent);\r
7726   ovl->Offset = ovl->OffsetHigh = 0;\r
7727   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7728   if (ok) {\r
7729     err = NO_ERROR;\r
7730   } else {\r
7731     err = GetLastError();\r
7732     if (err == ERROR_IO_PENDING) {\r
7733       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7734       if (ok)\r
7735         err = NO_ERROR;\r
7736       else\r
7737         err = GetLastError();\r
7738     }\r
7739   }\r
7740   return err;\r
7741 }\r
7742 \r
7743 int\r
7744 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7745             OVERLAPPED *ovl)\r
7746 {\r
7747   int ok, err;\r
7748 \r
7749   ResetEvent(ovl->hEvent);\r
7750   ovl->Offset = ovl->OffsetHigh = 0;\r
7751   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7752   if (ok) {\r
7753     err = NO_ERROR;\r
7754   } else {\r
7755     err = GetLastError();\r
7756     if (err == ERROR_IO_PENDING) {\r
7757       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7758       if (ok)\r
7759         err = NO_ERROR;\r
7760       else\r
7761         err = GetLastError();\r
7762     }\r
7763 \r
7764   }\r
7765   return err;\r
7766 }\r
7767 \r
7768 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7769 void CheckForInputBufferFull( InputSource * is )\r
7770 {\r
7771     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7772         /* Look for end of line */\r
7773         char * p = is->buf;\r
7774         \r
7775         while( p < is->next && *p != '\n' ) {\r
7776             p++;\r
7777         }\r
7778 \r
7779         if( p >= is->next ) {\r
7780             if (appData.debugMode) {\r
7781                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7782             }\r
7783 \r
7784             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7785             is->count = (DWORD) -1;\r
7786             is->next = is->buf;\r
7787         }\r
7788     }\r
7789 }\r
7790 \r
7791 DWORD\r
7792 InputThread(LPVOID arg)\r
7793 {\r
7794   InputSource *is;\r
7795   OVERLAPPED ovl;\r
7796 \r
7797   is = (InputSource *) arg;\r
7798   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7799   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7800   while (is->hThread != NULL) {\r
7801     is->error = DoReadFile(is->hFile, is->next,\r
7802                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7803                            &is->count, &ovl);\r
7804     if (is->error == NO_ERROR) {\r
7805       is->next += is->count;\r
7806     } else {\r
7807       if (is->error == ERROR_BROKEN_PIPE) {\r
7808         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7809         is->count = 0;\r
7810       } else {\r
7811         is->count = (DWORD) -1;\r
7812         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7813         break; \r
7814       }\r
7815     }\r
7816 \r
7817     CheckForInputBufferFull( is );\r
7818 \r
7819     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7820 \r
7821     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7822 \r
7823     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7824   }\r
7825 \r
7826   CloseHandle(ovl.hEvent);\r
7827   CloseHandle(is->hFile);\r
7828 \r
7829   if (appData.debugMode) {\r
7830     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7831   }\r
7832 \r
7833   return 0;\r
7834 }\r
7835 \r
7836 \r
7837 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7838 DWORD\r
7839 NonOvlInputThread(LPVOID arg)\r
7840 {\r
7841   InputSource *is;\r
7842   char *p, *q;\r
7843   int i;\r
7844   char prev;\r
7845 \r
7846   is = (InputSource *) arg;\r
7847   while (is->hThread != NULL) {\r
7848     is->error = ReadFile(is->hFile, is->next,\r
7849                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7850                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7851     if (is->error == NO_ERROR) {\r
7852       /* Change CRLF to LF */\r
7853       if (is->next > is->buf) {\r
7854         p = is->next - 1;\r
7855         i = is->count + 1;\r
7856       } else {\r
7857         p = is->next;\r
7858         i = is->count;\r
7859       }\r
7860       q = p;\r
7861       prev = NULLCHAR;\r
7862       while (i > 0) {\r
7863         if (prev == '\r' && *p == '\n') {\r
7864           *(q-1) = '\n';\r
7865           is->count--;\r
7866         } else { \r
7867           *q++ = *p;\r
7868         }\r
7869         prev = *p++;\r
7870         i--;\r
7871       }\r
7872       *q = NULLCHAR;\r
7873       is->next = q;\r
7874     } else {\r
7875       if (is->error == ERROR_BROKEN_PIPE) {\r
7876         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7877         is->count = 0; \r
7878       } else {\r
7879         is->count = (DWORD) -1;\r
7880       }\r
7881     }\r
7882 \r
7883     CheckForInputBufferFull( is );\r
7884 \r
7885     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7886 \r
7887     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7888 \r
7889     if (is->count < 0) break;  /* Quit on error */\r
7890   }\r
7891   CloseHandle(is->hFile);\r
7892   return 0;\r
7893 }\r
7894 \r
7895 DWORD\r
7896 SocketInputThread(LPVOID arg)\r
7897 {\r
7898   InputSource *is;\r
7899 \r
7900   is = (InputSource *) arg;\r
7901   while (is->hThread != NULL) {\r
7902     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7903     if ((int)is->count == SOCKET_ERROR) {\r
7904       is->count = (DWORD) -1;\r
7905       is->error = WSAGetLastError();\r
7906     } else {\r
7907       is->error = NO_ERROR;\r
7908       is->next += is->count;\r
7909       if (is->count == 0 && is->second == is) {\r
7910         /* End of file on stderr; quit with no message */\r
7911         break;\r
7912       }\r
7913     }\r
7914     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7915 \r
7916     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7917 \r
7918     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7919   }\r
7920   return 0;\r
7921 }\r
7922 \r
7923 VOID\r
7924 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7925 {\r
7926   InputSource *is;\r
7927 \r
7928   is = (InputSource *) lParam;\r
7929   if (is->lineByLine) {\r
7930     /* Feed in lines one by one */\r
7931     char *p = is->buf;\r
7932     char *q = p;\r
7933     while (q < is->next) {\r
7934       if (*q++ == '\n') {\r
7935         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7936         p = q;\r
7937       }\r
7938     }\r
7939     \r
7940     /* Move any partial line to the start of the buffer */\r
7941     q = is->buf;\r
7942     while (p < is->next) {\r
7943       *q++ = *p++;\r
7944     }\r
7945     is->next = q;\r
7946 \r
7947     if (is->error != NO_ERROR || is->count == 0) {\r
7948       /* Notify backend of the error.  Note: If there was a partial\r
7949          line at the end, it is not flushed through. */\r
7950       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7951     }\r
7952   } else {\r
7953     /* Feed in the whole chunk of input at once */\r
7954     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7955     is->next = is->buf;\r
7956   }\r
7957 }\r
7958 \r
7959 /*---------------------------------------------------------------------------*\\r
7960  *\r
7961  *  Menu enables. Used when setting various modes.\r
7962  *\r
7963 \*---------------------------------------------------------------------------*/\r
7964 \r
7965 typedef struct {\r
7966   int item;\r
7967   int flags;\r
7968 } Enables;\r
7969 \r
7970 VOID\r
7971 GreyRevert(Boolean grey)\r
7972 { // [HGM] vari: for retracting variations in local mode\r
7973   HMENU hmenu = GetMenu(hwndMain);\r
7974   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7975   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7976 }\r
7977 \r
7978 VOID\r
7979 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7980 {\r
7981   while (enab->item > 0) {\r
7982     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7983     enab++;\r
7984   }\r
7985 }\r
7986 \r
7987 Enables gnuEnables[] = {\r
7988   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7989   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7990   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7991   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7992   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7993   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7994   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7995   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7996   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7997   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7998   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7999   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8000   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8001 \r
8002 \r
8003   // Needed to switch from ncp to GNU mode on Engine Load\r
8004   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8005   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8006   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8007   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8008   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8009   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8010   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
8011   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
8012   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
8013   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
8014   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8015   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8016   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8017   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8018   { -1, -1 }\r
8019 };\r
8020 \r
8021 Enables icsEnables[] = {\r
8022   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8023   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8024   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8025   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8026   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8027   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8028   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8029   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8030   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8031   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8032   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8033   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8034   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8035   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
8036   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
8037   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8038   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8039   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8040   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8041   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
8042   { -1, -1 }\r
8043 };\r
8044 \r
8045 #if ZIPPY\r
8046 Enables zippyEnables[] = {\r
8047   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8048   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8049   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8050   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
8051   { -1, -1 }\r
8052 };\r
8053 #endif\r
8054 \r
8055 Enables ncpEnables[] = {\r
8056   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8057   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8058   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8059   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8060   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8061   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8062   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8063   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8064   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8065   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8066   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8067   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8068   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8069   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8070   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8071   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8072   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8073   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8074   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8075   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8076   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8077   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
8078   { -1, -1 }\r
8079 };\r
8080 \r
8081 Enables trainingOnEnables[] = {\r
8082   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8083   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
8084   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8085   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8086   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8087   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8088   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8089   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8090   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8091   { -1, -1 }\r
8092 };\r
8093 \r
8094 Enables trainingOffEnables[] = {\r
8095   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8096   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
8097   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8098   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8099   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8100   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8101   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8102   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8103   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8104   { -1, -1 }\r
8105 };\r
8106 \r
8107 /* These modify either ncpEnables or gnuEnables */\r
8108 Enables cmailEnables[] = {\r
8109   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8110   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8111   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8112   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8113   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8114   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8115   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8116   { -1, -1 }\r
8117 };\r
8118 \r
8119 Enables machineThinkingEnables[] = {\r
8120   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8121   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8122   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8123   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8124   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8125   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8126   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8127   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8128   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8129   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8130   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8131   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8132   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8133 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8134   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8135   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8136   { -1, -1 }\r
8137 };\r
8138 \r
8139 Enables userThinkingEnables[] = {\r
8140   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8141   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8142   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8143   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8144   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8145   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8146   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8147   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8148   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8149   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8150   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8151   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8152   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8153 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8154   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8155   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8156   { -1, -1 }\r
8157 };\r
8158 \r
8159 /*---------------------------------------------------------------------------*\\r
8160  *\r
8161  *  Front-end interface functions exported by XBoard.\r
8162  *  Functions appear in same order as prototypes in frontend.h.\r
8163  * \r
8164 \*---------------------------------------------------------------------------*/\r
8165 VOID\r
8166 CheckMark(UINT item, int state)\r
8167 {\r
8168     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
8169 }\r
8170 \r
8171 VOID\r
8172 ModeHighlight()\r
8173 {\r
8174   static UINT prevChecked = 0;\r
8175   static int prevPausing = 0;\r
8176   UINT nowChecked;\r
8177 \r
8178   if (pausing != prevPausing) {\r
8179     prevPausing = pausing;\r
8180     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8181                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8182     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8183   }\r
8184 \r
8185   switch (gameMode) {\r
8186   case BeginningOfGame:\r
8187     if (appData.icsActive)\r
8188       nowChecked = IDM_IcsClient;\r
8189     else if (appData.noChessProgram)\r
8190       nowChecked = IDM_EditGame;\r
8191     else\r
8192       nowChecked = IDM_MachineBlack;\r
8193     break;\r
8194   case MachinePlaysBlack:\r
8195     nowChecked = IDM_MachineBlack;\r
8196     break;\r
8197   case MachinePlaysWhite:\r
8198     nowChecked = IDM_MachineWhite;\r
8199     break;\r
8200   case TwoMachinesPlay:\r
8201     nowChecked = IDM_TwoMachines;\r
8202     break;\r
8203   case AnalyzeMode:\r
8204     nowChecked = IDM_AnalysisMode;\r
8205     break;\r
8206   case AnalyzeFile:\r
8207     nowChecked = IDM_AnalyzeFile;\r
8208     break;\r
8209   case EditGame:\r
8210     nowChecked = IDM_EditGame;\r
8211     break;\r
8212   case PlayFromGameFile:\r
8213     nowChecked = IDM_LoadGame;\r
8214     break;\r
8215   case EditPosition:\r
8216     nowChecked = IDM_EditPosition;\r
8217     break;\r
8218   case Training:\r
8219     nowChecked = IDM_Training;\r
8220     break;\r
8221   case IcsPlayingWhite:\r
8222   case IcsPlayingBlack:\r
8223   case IcsObserving:\r
8224   case IcsIdle:\r
8225     nowChecked = IDM_IcsClient;\r
8226     break;\r
8227   default:\r
8228   case EndOfGame:\r
8229     nowChecked = 0;\r
8230     break;\r
8231   }\r
8232   if(prevChecked == IDM_TwoMachine) // [HGM] 'Machine Match' might have gotten disabled when stopping match\r
8233     EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_ENABLED)\r
8234   CheckMark(prevChecked, MF_UNCHECKED);\r
8235   CheckMark(nowChecked, MF_CHECKED);\r
8236   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8237 \r
8238   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8239     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8240                           MF_BYCOMMAND|MF_ENABLED);\r
8241   } else {\r
8242     (void) EnableMenuItem(GetMenu(hwndMain), \r
8243                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8244   }\r
8245 \r
8246   prevChecked = nowChecked;\r
8247 \r
8248   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8249   if (appData.icsActive) {\r
8250        if (appData.icsEngineAnalyze) {\r
8251                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8252        } else {\r
8253                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8254        }\r
8255   }\r
8256   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8257 }\r
8258 \r
8259 VOID\r
8260 SetICSMode()\r
8261 {\r
8262   HMENU hmenu = GetMenu(hwndMain);\r
8263   SetMenuEnables(hmenu, icsEnables);\r
8264   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8265     MF_BYCOMMAND|MF_ENABLED);\r
8266 #if ZIPPY\r
8267   if (appData.zippyPlay) {\r
8268     SetMenuEnables(hmenu, zippyEnables);\r
8269     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8270          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8271           MF_BYCOMMAND|MF_ENABLED);\r
8272   }\r
8273 #endif\r
8274 }\r
8275 \r
8276 VOID\r
8277 SetGNUMode()\r
8278 {\r
8279   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8280 }\r
8281 \r
8282 VOID\r
8283 SetNCPMode()\r
8284 {\r
8285   HMENU hmenu = GetMenu(hwndMain);\r
8286   SetMenuEnables(hmenu, ncpEnables);\r
8287     DrawMenuBar(hwndMain);\r
8288 }\r
8289 \r
8290 VOID\r
8291 SetCmailMode()\r
8292 {\r
8293   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8294 }\r
8295 \r
8296 VOID \r
8297 SetTrainingModeOn()\r
8298 {\r
8299   int i;\r
8300   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8301   for (i = 0; i < N_BUTTONS; i++) {\r
8302     if (buttonDesc[i].hwnd != NULL)\r
8303       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8304   }\r
8305   CommentPopDown();\r
8306 }\r
8307 \r
8308 VOID SetTrainingModeOff()\r
8309 {\r
8310   int i;\r
8311   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8312   for (i = 0; i < N_BUTTONS; i++) {\r
8313     if (buttonDesc[i].hwnd != NULL)\r
8314       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8315   }\r
8316 }\r
8317 \r
8318 \r
8319 VOID\r
8320 SetUserThinkingEnables()\r
8321 {\r
8322   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8323 }\r
8324 \r
8325 VOID\r
8326 SetMachineThinkingEnables()\r
8327 {\r
8328   HMENU hMenu = GetMenu(hwndMain);\r
8329   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8330 \r
8331   SetMenuEnables(hMenu, machineThinkingEnables);\r
8332 \r
8333   if (gameMode == MachinePlaysBlack) {\r
8334     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8335   } else if (gameMode == MachinePlaysWhite) {\r
8336     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8337   } else if (gameMode == TwoMachinesPlay) {\r
8338     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8339   }\r
8340 }\r
8341 \r
8342 \r
8343 VOID\r
8344 DisplayTitle(char *str)\r
8345 {\r
8346   char title[MSG_SIZ], *host;\r
8347   if (str[0] != NULLCHAR) {\r
8348     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8349   } else if (appData.icsActive) {\r
8350     if (appData.icsCommPort[0] != NULLCHAR)\r
8351       host = "ICS";\r
8352     else \r
8353       host = appData.icsHost;\r
8354       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8355   } else if (appData.noChessProgram) {\r
8356     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8357   } else {\r
8358     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8359     strcat(title, ": ");\r
8360     strcat(title, first.tidy);\r
8361   }\r
8362   SetWindowText(hwndMain, title);\r
8363 }\r
8364 \r
8365 \r
8366 VOID\r
8367 DisplayMessage(char *str1, char *str2)\r
8368 {\r
8369   HDC hdc;\r
8370   HFONT oldFont;\r
8371   int remain = MESSAGE_TEXT_MAX - 1;\r
8372   int len;\r
8373 \r
8374   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8375   messageText[0] = NULLCHAR;\r
8376   if (*str1) {\r
8377     len = strlen(str1);\r
8378     if (len > remain) len = remain;\r
8379     strncpy(messageText, str1, len);\r
8380     messageText[len] = NULLCHAR;\r
8381     remain -= len;\r
8382   }\r
8383   if (*str2 && remain >= 2) {\r
8384     if (*str1) {\r
8385       strcat(messageText, "  ");\r
8386       remain -= 2;\r
8387     }\r
8388     len = strlen(str2);\r
8389     if (len > remain) len = remain;\r
8390     strncat(messageText, str2, len);\r
8391   }\r
8392   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8393   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8394 \r
8395   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8396 \r
8397   SAYMACHINEMOVE();\r
8398 \r
8399   hdc = GetDC(hwndMain);\r
8400   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8401   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8402              &messageRect, messageText, strlen(messageText), NULL);\r
8403   (void) SelectObject(hdc, oldFont);\r
8404   (void) ReleaseDC(hwndMain, hdc);\r
8405 }\r
8406 \r
8407 VOID\r
8408 DisplayError(char *str, int error)\r
8409 {\r
8410   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8411   int len;\r
8412 \r
8413   if (error == 0) {\r
8414     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8415   } else {\r
8416     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8417                         NULL, error, LANG_NEUTRAL,\r
8418                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8419     if (len > 0) {\r
8420       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8421     } else {\r
8422       ErrorMap *em = errmap;\r
8423       while (em->err != 0 && em->err != error) em++;\r
8424       if (em->err != 0) {\r
8425         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8426       } else {\r
8427         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8428       }\r
8429     }\r
8430   }\r
8431   \r
8432   ErrorPopUp(_("Error"), buf);\r
8433 }\r
8434 \r
8435 \r
8436 VOID\r
8437 DisplayMoveError(char *str)\r
8438 {\r
8439   fromX = fromY = -1;\r
8440   ClearHighlights();\r
8441   DrawPosition(FALSE, NULL);\r
8442   if (appData.popupMoveErrors) {\r
8443     ErrorPopUp(_("Error"), str);\r
8444   } else {\r
8445     DisplayMessage(str, "");\r
8446     moveErrorMessageUp = TRUE;\r
8447   }\r
8448 }\r
8449 \r
8450 VOID\r
8451 DisplayFatalError(char *str, int error, int exitStatus)\r
8452 {\r
8453   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8454   int len;\r
8455   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8456 \r
8457   if (error != 0) {\r
8458     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8459                         NULL, error, LANG_NEUTRAL,\r
8460                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8461     if (len > 0) {\r
8462       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8463     } else {\r
8464       ErrorMap *em = errmap;\r
8465       while (em->err != 0 && em->err != error) em++;\r
8466       if (em->err != 0) {\r
8467         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8468       } else {\r
8469         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8470       }\r
8471     }\r
8472     str = buf;\r
8473   }\r
8474   if (appData.debugMode) {\r
8475     fprintf(debugFP, "%s: %s\n", label, str);\r
8476   }\r
8477   if (appData.popupExitMessage) {\r
8478     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8479                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8480   }\r
8481   ExitEvent(exitStatus);\r
8482 }\r
8483 \r
8484 \r
8485 VOID\r
8486 DisplayInformation(char *str)\r
8487 {\r
8488   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8489 }\r
8490 \r
8491 \r
8492 VOID\r
8493 DisplayNote(char *str)\r
8494 {\r
8495   ErrorPopUp(_("Note"), str);\r
8496 }\r
8497 \r
8498 \r
8499 typedef struct {\r
8500   char *title, *question, *replyPrefix;\r
8501   ProcRef pr;\r
8502 } QuestionParams;\r
8503 \r
8504 LRESULT CALLBACK\r
8505 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8506 {\r
8507   static QuestionParams *qp;\r
8508   char reply[MSG_SIZ];\r
8509   int len, err;\r
8510 \r
8511   switch (message) {\r
8512   case WM_INITDIALOG:\r
8513     qp = (QuestionParams *) lParam;\r
8514     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8515     Translate(hDlg, DLG_Question);\r
8516     SetWindowText(hDlg, qp->title);\r
8517     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8518     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8519     return FALSE;\r
8520 \r
8521   case WM_COMMAND:\r
8522     switch (LOWORD(wParam)) {\r
8523     case IDOK:\r
8524       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8525       if (*reply) strcat(reply, " ");\r
8526       len = strlen(reply);\r
8527       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8528       strcat(reply, "\n");\r
8529       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8530       EndDialog(hDlg, TRUE);\r
8531       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8532       return TRUE;\r
8533     case IDCANCEL:\r
8534       EndDialog(hDlg, FALSE);\r
8535       return TRUE;\r
8536     default:\r
8537       break;\r
8538     }\r
8539     break;\r
8540   }\r
8541   return FALSE;\r
8542 }\r
8543 \r
8544 VOID\r
8545 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8546 {\r
8547     QuestionParams qp;\r
8548     FARPROC lpProc;\r
8549     \r
8550     qp.title = title;\r
8551     qp.question = question;\r
8552     qp.replyPrefix = replyPrefix;\r
8553     qp.pr = pr;\r
8554     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8555     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8556       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8557     FreeProcInstance(lpProc);\r
8558 }\r
8559 \r
8560 /* [AS] Pick FRC position */\r
8561 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8562 {\r
8563     static int * lpIndexFRC;\r
8564     BOOL index_is_ok;\r
8565     char buf[16];\r
8566 \r
8567     switch( message )\r
8568     {\r
8569     case WM_INITDIALOG:\r
8570         lpIndexFRC = (int *) lParam;\r
8571 \r
8572         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8573         Translate(hDlg, DLG_NewGameFRC);\r
8574 \r
8575         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8576         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8577         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8578         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8579 \r
8580         break;\r
8581 \r
8582     case WM_COMMAND:\r
8583         switch( LOWORD(wParam) ) {\r
8584         case IDOK:\r
8585             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8586             EndDialog( hDlg, 0 );\r
8587             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8588             return TRUE;\r
8589         case IDCANCEL:\r
8590             EndDialog( hDlg, 1 );   \r
8591             return TRUE;\r
8592         case IDC_NFG_Edit:\r
8593             if( HIWORD(wParam) == EN_CHANGE ) {\r
8594                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8595 \r
8596                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8597             }\r
8598             return TRUE;\r
8599         case IDC_NFG_Random:\r
8600           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8601             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8602             return TRUE;\r
8603         }\r
8604 \r
8605         break;\r
8606     }\r
8607 \r
8608     return FALSE;\r
8609 }\r
8610 \r
8611 int NewGameFRC()\r
8612 {\r
8613     int result;\r
8614     int index = appData.defaultFrcPosition;\r
8615     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8616 \r
8617     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8618 \r
8619     if( result == 0 ) {\r
8620         appData.defaultFrcPosition = index;\r
8621     }\r
8622 \r
8623     return result;\r
8624 }\r
8625 \r
8626 /* [AS] Game list options. Refactored by HGM */\r
8627 \r
8628 HWND gameListOptionsDialog;\r
8629 \r
8630 // low-level front-end: clear text edit / list widget\r
8631 void\r
8632 \r
8633 GLT_ClearList()\r
8634 {\r
8635     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8636 }\r
8637 \r
8638 // low-level front-end: clear text edit / list widget\r
8639 void\r
8640 GLT_DeSelectList()\r
8641 {\r
8642     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8643 }\r
8644 \r
8645 // low-level front-end: append line to text edit / list widget\r
8646 void\r
8647 GLT_AddToList( char *name )\r
8648 {\r
8649     if( name != 0 ) {\r
8650             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8651     }\r
8652 }\r
8653 \r
8654 // low-level front-end: get line from text edit / list widget\r
8655 Boolean\r
8656 GLT_GetFromList( int index, char *name )\r
8657 {\r
8658     if( name != 0 ) {\r
8659             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8660                 return TRUE;\r
8661     }\r
8662     return FALSE;\r
8663 }\r
8664 \r
8665 void GLT_MoveSelection( HWND hDlg, int delta )\r
8666 {\r
8667     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8668     int idx2 = idx1 + delta;\r
8669     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8670 \r
8671     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8672         char buf[128];\r
8673 \r
8674         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8675         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8676         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8677         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8678     }\r
8679 }\r
8680 \r
8681 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8682 {\r
8683     switch( message )\r
8684     {\r
8685     case WM_INITDIALOG:\r
8686         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8687         \r
8688         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8689         Translate(hDlg, DLG_GameListOptions);\r
8690 \r
8691         /* Initialize list */\r
8692         GLT_TagsToList( lpUserGLT );\r
8693 \r
8694         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8695 \r
8696         break;\r
8697 \r
8698     case WM_COMMAND:\r
8699         switch( LOWORD(wParam) ) {\r
8700         case IDOK:\r
8701             GLT_ParseList();\r
8702             EndDialog( hDlg, 0 );\r
8703             return TRUE;\r
8704         case IDCANCEL:\r
8705             EndDialog( hDlg, 1 );\r
8706             return TRUE;\r
8707 \r
8708         case IDC_GLT_Default:\r
8709             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8710             return TRUE;\r
8711 \r
8712         case IDC_GLT_Restore:\r
8713             GLT_TagsToList( appData.gameListTags );\r
8714             return TRUE;\r
8715 \r
8716         case IDC_GLT_Up:\r
8717             GLT_MoveSelection( hDlg, -1 );\r
8718             return TRUE;\r
8719 \r
8720         case IDC_GLT_Down:\r
8721             GLT_MoveSelection( hDlg, +1 );\r
8722             return TRUE;\r
8723         }\r
8724 \r
8725         break;\r
8726     }\r
8727 \r
8728     return FALSE;\r
8729 }\r
8730 \r
8731 int GameListOptions()\r
8732 {\r
8733     int result;\r
8734     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8735 \r
8736       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8737 \r
8738     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8739 \r
8740     if( result == 0 ) {\r
8741         char *oldTags = appData.gameListTags;\r
8742         /* [AS] Memory leak here! */\r
8743         appData.gameListTags = strdup( lpUserGLT ); \r
8744         if(strcmp(oldTags, appData.gameListTags)) // [HGM] redo Game List when we changed something\r
8745             GameListToListBox(NULL, TRUE, ".", NULL, FALSE, FALSE); // "." as filter is kludge to select all\r
8746     }\r
8747 \r
8748     return result;\r
8749 }\r
8750 \r
8751 VOID\r
8752 DisplayIcsInteractionTitle(char *str)\r
8753 {\r
8754   char consoleTitle[MSG_SIZ];\r
8755 \r
8756     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8757     SetWindowText(hwndConsole, consoleTitle);\r
8758 \r
8759     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8760       char buf[MSG_SIZ], *p = buf, *q;\r
8761         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8762       do {\r
8763         q = strchr(p, ';');\r
8764         if(q) *q++ = 0;\r
8765         if(*p) ChatPopUp(p);\r
8766       } while(p=q);\r
8767     }\r
8768 \r
8769     SetActiveWindow(hwndMain);\r
8770 }\r
8771 \r
8772 void\r
8773 DrawPosition(int fullRedraw, Board board)\r
8774 {\r
8775   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8776 }\r
8777 \r
8778 void NotifyFrontendLogin()\r
8779 {\r
8780         if (hwndConsole)\r
8781                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8782 }\r
8783 \r
8784 VOID\r
8785 ResetFrontEnd()\r
8786 {\r
8787   fromX = fromY = -1;\r
8788   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8789     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8790     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8791     dragInfo.lastpos = dragInfo.pos;\r
8792     dragInfo.start.x = dragInfo.start.y = -1;\r
8793     dragInfo.from = dragInfo.start;\r
8794     ReleaseCapture();\r
8795     DrawPosition(TRUE, NULL);\r
8796   }\r
8797   TagsPopDown();\r
8798 }\r
8799 \r
8800 \r
8801 VOID\r
8802 CommentPopUp(char *title, char *str)\r
8803 {\r
8804   HWND hwnd = GetActiveWindow();\r
8805   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8806   SAY(str);\r
8807   SetActiveWindow(hwnd);\r
8808 }\r
8809 \r
8810 VOID\r
8811 CommentPopDown(void)\r
8812 {\r
8813   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8814   if (commentDialog) {\r
8815     ShowWindow(commentDialog, SW_HIDE);\r
8816   }\r
8817   commentUp = FALSE;\r
8818 }\r
8819 \r
8820 VOID\r
8821 EditCommentPopUp(int index, char *title, char *str)\r
8822 {\r
8823   EitherCommentPopUp(index, title, str, TRUE);\r
8824 }\r
8825 \r
8826 \r
8827 int\r
8828 Roar()\r
8829 {\r
8830   MyPlaySound(&sounds[(int)SoundRoar]);\r
8831   return 1;\r
8832 }\r
8833 \r
8834 VOID\r
8835 RingBell()\r
8836 {\r
8837   MyPlaySound(&sounds[(int)SoundMove]);\r
8838 }\r
8839 \r
8840 VOID PlayIcsWinSound()\r
8841 {\r
8842   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8843 }\r
8844 \r
8845 VOID PlayIcsLossSound()\r
8846 {\r
8847   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8848 }\r
8849 \r
8850 VOID PlayIcsDrawSound()\r
8851 {\r
8852   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8853 }\r
8854 \r
8855 VOID PlayIcsUnfinishedSound()\r
8856 {\r
8857   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8858 }\r
8859 \r
8860 VOID\r
8861 PlayAlarmSound()\r
8862 {\r
8863   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8864 }\r
8865 \r
8866 VOID\r
8867 PlayTellSound()\r
8868 {\r
8869   MyPlaySound(&textAttribs[ColorTell].sound);\r
8870 }\r
8871 \r
8872 \r
8873 VOID\r
8874 EchoOn()\r
8875 {\r
8876   HWND hInput;\r
8877   consoleEcho = TRUE;\r
8878   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8879   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8880   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8881 }\r
8882 \r
8883 \r
8884 VOID\r
8885 EchoOff()\r
8886 {\r
8887   CHARFORMAT cf;\r
8888   HWND hInput;\r
8889   consoleEcho = FALSE;\r
8890   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8891   /* This works OK: set text and background both to the same color */\r
8892   cf = consoleCF;\r
8893   cf.crTextColor = COLOR_ECHOOFF;\r
8894   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8895   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8896 }\r
8897 \r
8898 /* No Raw()...? */\r
8899 \r
8900 void Colorize(ColorClass cc, int continuation)\r
8901 {\r
8902   currentColorClass = cc;\r
8903   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8904   consoleCF.crTextColor = textAttribs[cc].color;\r
8905   consoleCF.dwEffects = textAttribs[cc].effects;\r
8906   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8907 }\r
8908 \r
8909 char *\r
8910 UserName()\r
8911 {\r
8912   static char buf[MSG_SIZ];\r
8913   DWORD bufsiz = MSG_SIZ;\r
8914 \r
8915   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8916         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8917   }\r
8918   if (!GetUserName(buf, &bufsiz)) {\r
8919     /*DisplayError("Error getting user name", GetLastError());*/\r
8920     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8921   }\r
8922   return buf;\r
8923 }\r
8924 \r
8925 char *\r
8926 HostName()\r
8927 {\r
8928   static char buf[MSG_SIZ];\r
8929   DWORD bufsiz = MSG_SIZ;\r
8930 \r
8931   if (!GetComputerName(buf, &bufsiz)) {\r
8932     /*DisplayError("Error getting host name", GetLastError());*/\r
8933     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8934   }\r
8935   return buf;\r
8936 }\r
8937 \r
8938 \r
8939 int\r
8940 ClockTimerRunning()\r
8941 {\r
8942   return clockTimerEvent != 0;\r
8943 }\r
8944 \r
8945 int\r
8946 StopClockTimer()\r
8947 {\r
8948   if (clockTimerEvent == 0) return FALSE;\r
8949   KillTimer(hwndMain, clockTimerEvent);\r
8950   clockTimerEvent = 0;\r
8951   return TRUE;\r
8952 }\r
8953 \r
8954 void\r
8955 StartClockTimer(long millisec)\r
8956 {\r
8957   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8958                              (UINT) millisec, NULL);\r
8959 }\r
8960 \r
8961 void\r
8962 DisplayWhiteClock(long timeRemaining, int highlight)\r
8963 {\r
8964   HDC hdc;\r
8965   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8966 \r
8967   if(appData.noGUI) return;\r
8968   hdc = GetDC(hwndMain);\r
8969   if (!IsIconic(hwndMain)) {\r
8970     DisplayAClock(hdc, timeRemaining, highlight, \r
8971                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8972   }\r
8973   if (highlight && iconCurrent == iconBlack) {\r
8974     iconCurrent = iconWhite;\r
8975     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8976     if (IsIconic(hwndMain)) {\r
8977       DrawIcon(hdc, 2, 2, iconCurrent);\r
8978     }\r
8979   }\r
8980   (void) ReleaseDC(hwndMain, hdc);\r
8981   if (hwndConsole)\r
8982     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8983 }\r
8984 \r
8985 void\r
8986 DisplayBlackClock(long timeRemaining, int highlight)\r
8987 {\r
8988   HDC hdc;\r
8989   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8990 \r
8991 \r
8992   if(appData.noGUI) return;\r
8993   hdc = GetDC(hwndMain);\r
8994   if (!IsIconic(hwndMain)) {\r
8995     DisplayAClock(hdc, timeRemaining, highlight, \r
8996                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8997   }\r
8998   if (highlight && iconCurrent == iconWhite) {\r
8999     iconCurrent = iconBlack;\r
9000     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9001     if (IsIconic(hwndMain)) {\r
9002       DrawIcon(hdc, 2, 2, iconCurrent);\r
9003     }\r
9004   }\r
9005   (void) ReleaseDC(hwndMain, hdc);\r
9006   if (hwndConsole)\r
9007     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9008 }\r
9009 \r
9010 \r
9011 int\r
9012 LoadGameTimerRunning()\r
9013 {\r
9014   return loadGameTimerEvent != 0;\r
9015 }\r
9016 \r
9017 int\r
9018 StopLoadGameTimer()\r
9019 {\r
9020   if (loadGameTimerEvent == 0) return FALSE;\r
9021   KillTimer(hwndMain, loadGameTimerEvent);\r
9022   loadGameTimerEvent = 0;\r
9023   return TRUE;\r
9024 }\r
9025 \r
9026 void\r
9027 StartLoadGameTimer(long millisec)\r
9028 {\r
9029   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9030                                 (UINT) millisec, NULL);\r
9031 }\r
9032 \r
9033 void\r
9034 AutoSaveGame()\r
9035 {\r
9036   char *defName;\r
9037   FILE *f;\r
9038   char fileTitle[MSG_SIZ];\r
9039 \r
9040   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9041   f = OpenFileDialog(hwndMain, "a", defName,\r
9042                      appData.oldSaveStyle ? "gam" : "pgn",\r
9043                      GAME_FILT, \r
9044                      _("Save Game to File"), NULL, fileTitle, NULL);\r
9045   if (f != NULL) {\r
9046     SaveGame(f, 0, "");\r
9047     fclose(f);\r
9048   }\r
9049 }\r
9050 \r
9051 \r
9052 void\r
9053 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9054 {\r
9055   if (delayedTimerEvent != 0) {\r
9056     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
9057       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9058     }\r
9059     KillTimer(hwndMain, delayedTimerEvent);\r
9060     delayedTimerEvent = 0;\r
9061     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
9062     delayedTimerCallback();\r
9063   }\r
9064   delayedTimerCallback = cb;\r
9065   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9066                                 (UINT) millisec, NULL);\r
9067 }\r
9068 \r
9069 DelayedEventCallback\r
9070 GetDelayedEvent()\r
9071 {\r
9072   if (delayedTimerEvent) {\r
9073     return delayedTimerCallback;\r
9074   } else {\r
9075     return NULL;\r
9076   }\r
9077 }\r
9078 \r
9079 void\r
9080 CancelDelayedEvent()\r
9081 {\r
9082   if (delayedTimerEvent) {\r
9083     KillTimer(hwndMain, delayedTimerEvent);\r
9084     delayedTimerEvent = 0;\r
9085   }\r
9086 }\r
9087 \r
9088 DWORD GetWin32Priority(int nice)\r
9089 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9090 /*\r
9091 REALTIME_PRIORITY_CLASS     0x00000100\r
9092 HIGH_PRIORITY_CLASS         0x00000080\r
9093 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9094 NORMAL_PRIORITY_CLASS       0x00000020\r
9095 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9096 IDLE_PRIORITY_CLASS         0x00000040\r
9097 */\r
9098         if (nice < -15) return 0x00000080;\r
9099         if (nice < 0)   return 0x00008000;\r
9100         if (nice == 0)  return 0x00000020;\r
9101         if (nice < 15)  return 0x00004000;\r
9102         return 0x00000040;\r
9103 }\r
9104 \r
9105 void RunCommand(char *cmdLine)\r
9106 {\r
9107   /* Now create the child process. */\r
9108   STARTUPINFO siStartInfo;\r
9109   PROCESS_INFORMATION piProcInfo;\r
9110 \r
9111   siStartInfo.cb = sizeof(STARTUPINFO);\r
9112   siStartInfo.lpReserved = NULL;\r
9113   siStartInfo.lpDesktop = NULL;\r
9114   siStartInfo.lpTitle = NULL;\r
9115   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9116   siStartInfo.cbReserved2 = 0;\r
9117   siStartInfo.lpReserved2 = NULL;\r
9118   siStartInfo.hStdInput = NULL;\r
9119   siStartInfo.hStdOutput = NULL;\r
9120   siStartInfo.hStdError = NULL;\r
9121 \r
9122   CreateProcess(NULL,\r
9123                 cmdLine,           /* command line */\r
9124                 NULL,      /* process security attributes */\r
9125                 NULL,      /* primary thread security attrs */\r
9126                 TRUE,      /* handles are inherited */\r
9127                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9128                 NULL,      /* use parent's environment */\r
9129                 NULL,\r
9130                 &siStartInfo, /* STARTUPINFO pointer */\r
9131                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
9132 \r
9133   CloseHandle(piProcInfo.hThread);\r
9134 }\r
9135 \r
9136 /* Start a child process running the given program.\r
9137    The process's standard output can be read from "from", and its\r
9138    standard input can be written to "to".\r
9139    Exit with fatal error if anything goes wrong.\r
9140    Returns an opaque pointer that can be used to destroy the process\r
9141    later.\r
9142 */\r
9143 int\r
9144 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9145 {\r
9146 #define BUFSIZE 4096\r
9147 \r
9148   HANDLE hChildStdinRd, hChildStdinWr,\r
9149     hChildStdoutRd, hChildStdoutWr;\r
9150   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9151   SECURITY_ATTRIBUTES saAttr;\r
9152   BOOL fSuccess;\r
9153   PROCESS_INFORMATION piProcInfo;\r
9154   STARTUPINFO siStartInfo;\r
9155   ChildProc *cp;\r
9156   char buf[MSG_SIZ];\r
9157   DWORD err;\r
9158 \r
9159   if (appData.debugMode) {\r
9160     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9161   }\r
9162 \r
9163   *pr = NoProc;\r
9164 \r
9165   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9166   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9167   saAttr.bInheritHandle = TRUE;\r
9168   saAttr.lpSecurityDescriptor = NULL;\r
9169 \r
9170   /*\r
9171    * The steps for redirecting child's STDOUT:\r
9172    *     1. Create anonymous pipe to be STDOUT for child.\r
9173    *     2. Create a noninheritable duplicate of read handle,\r
9174    *         and close the inheritable read handle.\r
9175    */\r
9176 \r
9177   /* Create a pipe for the child's STDOUT. */\r
9178   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9179     return GetLastError();\r
9180   }\r
9181 \r
9182   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9183   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9184                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9185                              FALSE,     /* not inherited */\r
9186                              DUPLICATE_SAME_ACCESS);\r
9187   if (! fSuccess) {\r
9188     return GetLastError();\r
9189   }\r
9190   CloseHandle(hChildStdoutRd);\r
9191 \r
9192   /*\r
9193    * The steps for redirecting child's STDIN:\r
9194    *     1. Create anonymous pipe to be STDIN for child.\r
9195    *     2. Create a noninheritable duplicate of write handle,\r
9196    *         and close the inheritable write handle.\r
9197    */\r
9198 \r
9199   /* Create a pipe for the child's STDIN. */\r
9200   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9201     return GetLastError();\r
9202   }\r
9203 \r
9204   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9205   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9206                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9207                              FALSE,     /* not inherited */\r
9208                              DUPLICATE_SAME_ACCESS);\r
9209   if (! fSuccess) {\r
9210     return GetLastError();\r
9211   }\r
9212   CloseHandle(hChildStdinWr);\r
9213 \r
9214   /* Arrange to (1) look in dir for the child .exe file, and\r
9215    * (2) have dir be the child's working directory.  Interpret\r
9216    * dir relative to the directory WinBoard loaded from. */\r
9217   GetCurrentDirectory(MSG_SIZ, buf);\r
9218   SetCurrentDirectory(installDir);\r
9219   SetCurrentDirectory(dir);\r
9220 \r
9221   /* Now create the child process. */\r
9222 \r
9223   siStartInfo.cb = sizeof(STARTUPINFO);\r
9224   siStartInfo.lpReserved = NULL;\r
9225   siStartInfo.lpDesktop = NULL;\r
9226   siStartInfo.lpTitle = NULL;\r
9227   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9228   siStartInfo.cbReserved2 = 0;\r
9229   siStartInfo.lpReserved2 = NULL;\r
9230   siStartInfo.hStdInput = hChildStdinRd;\r
9231   siStartInfo.hStdOutput = hChildStdoutWr;\r
9232   siStartInfo.hStdError = hChildStdoutWr;\r
9233 \r
9234   fSuccess = CreateProcess(NULL,\r
9235                            cmdLine,        /* command line */\r
9236                            NULL,           /* process security attributes */\r
9237                            NULL,           /* primary thread security attrs */\r
9238                            TRUE,           /* handles are inherited */\r
9239                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9240                            NULL,           /* use parent's environment */\r
9241                            NULL,\r
9242                            &siStartInfo, /* STARTUPINFO pointer */\r
9243                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9244 \r
9245   err = GetLastError();\r
9246   SetCurrentDirectory(buf); /* return to prev directory */\r
9247   if (! fSuccess) {\r
9248     return err;\r
9249   }\r
9250 \r
9251   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9252     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9253     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9254   }\r
9255 \r
9256   /* Close the handles we don't need in the parent */\r
9257   CloseHandle(piProcInfo.hThread);\r
9258   CloseHandle(hChildStdinRd);\r
9259   CloseHandle(hChildStdoutWr);\r
9260 \r
9261   /* Prepare return value */\r
9262   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9263   cp->kind = CPReal;\r
9264   cp->hProcess = piProcInfo.hProcess;\r
9265   cp->pid = piProcInfo.dwProcessId;\r
9266   cp->hFrom = hChildStdoutRdDup;\r
9267   cp->hTo = hChildStdinWrDup;\r
9268 \r
9269   *pr = (void *) cp;\r
9270 \r
9271   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9272      2000 where engines sometimes don't see the initial command(s)\r
9273      from WinBoard and hang.  I don't understand how that can happen,\r
9274      but the Sleep is harmless, so I've put it in.  Others have also\r
9275      reported what may be the same problem, so hopefully this will fix\r
9276      it for them too.  */\r
9277   Sleep(500);\r
9278 \r
9279   return NO_ERROR;\r
9280 }\r
9281 \r
9282 \r
9283 void\r
9284 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9285 {\r
9286   ChildProc *cp; int result;\r
9287 \r
9288   cp = (ChildProc *) pr;\r
9289   if (cp == NULL) return;\r
9290 \r
9291   switch (cp->kind) {\r
9292   case CPReal:\r
9293     /* TerminateProcess is considered harmful, so... */\r
9294     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9295     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9296     /* The following doesn't work because the chess program\r
9297        doesn't "have the same console" as WinBoard.  Maybe\r
9298        we could arrange for this even though neither WinBoard\r
9299        nor the chess program uses a console for stdio? */\r
9300     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9301 \r
9302     /* [AS] Special termination modes for misbehaving programs... */\r
9303     if( signal & 8 ) { \r
9304         result = TerminateProcess( cp->hProcess, 0 );\r
9305 \r
9306         if ( appData.debugMode) {\r
9307             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9308         }\r
9309     }\r
9310     else if( signal & 4 ) {\r
9311         DWORD dw = WaitForSingleObject( cp->hProcess, appData.delayAfterQuit*1000 + 50 ); // Wait 3 seconds at most\r
9312 \r
9313         if( dw != WAIT_OBJECT_0 ) {\r
9314             result = TerminateProcess( cp->hProcess, 0 );\r
9315 \r
9316             if ( appData.debugMode) {\r
9317                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9318             }\r
9319 \r
9320         }\r
9321     }\r
9322 \r
9323     CloseHandle(cp->hProcess);\r
9324     break;\r
9325 \r
9326   case CPComm:\r
9327     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9328     break;\r
9329 \r
9330   case CPSock:\r
9331     closesocket(cp->sock);\r
9332     WSACleanup();\r
9333     break;\r
9334 \r
9335   case CPRcmd:\r
9336     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9337     closesocket(cp->sock);\r
9338     closesocket(cp->sock2);\r
9339     WSACleanup();\r
9340     break;\r
9341   }\r
9342   free(cp);\r
9343 }\r
9344 \r
9345 void\r
9346 InterruptChildProcess(ProcRef pr)\r
9347 {\r
9348   ChildProc *cp;\r
9349 \r
9350   cp = (ChildProc *) pr;\r
9351   if (cp == NULL) return;\r
9352   switch (cp->kind) {\r
9353   case CPReal:\r
9354     /* The following doesn't work because the chess program\r
9355        doesn't "have the same console" as WinBoard.  Maybe\r
9356        we could arrange for this even though neither WinBoard\r
9357        nor the chess program uses a console for stdio */\r
9358     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9359     break;\r
9360 \r
9361   case CPComm:\r
9362   case CPSock:\r
9363     /* Can't interrupt */\r
9364     break;\r
9365 \r
9366   case CPRcmd:\r
9367     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9368     break;\r
9369   }\r
9370 }\r
9371 \r
9372 \r
9373 int\r
9374 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9375 {\r
9376   char cmdLine[MSG_SIZ];\r
9377 \r
9378   if (port[0] == NULLCHAR) {\r
9379     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9380   } else {\r
9381     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9382   }\r
9383   return StartChildProcess(cmdLine, "", pr);\r
9384 }\r
9385 \r
9386 \r
9387 /* Code to open TCP sockets */\r
9388 \r
9389 int\r
9390 OpenTCP(char *host, char *port, ProcRef *pr)\r
9391 {\r
9392   ChildProc *cp;\r
9393   int err;\r
9394   SOCKET s;\r
9395 \r
9396   struct sockaddr_in sa, mysa;\r
9397   struct hostent FAR *hp;\r
9398   unsigned short uport;\r
9399   WORD wVersionRequested;\r
9400   WSADATA wsaData;\r
9401 \r
9402   /* Initialize socket DLL */\r
9403   wVersionRequested = MAKEWORD(1, 1);\r
9404   err = WSAStartup(wVersionRequested, &wsaData);\r
9405   if (err != 0) return err;\r
9406 \r
9407   /* Make socket */\r
9408   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9409     err = WSAGetLastError();\r
9410     WSACleanup();\r
9411     return err;\r
9412   }\r
9413 \r
9414   /* Bind local address using (mostly) don't-care values.\r
9415    */\r
9416   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9417   mysa.sin_family = AF_INET;\r
9418   mysa.sin_addr.s_addr = INADDR_ANY;\r
9419   uport = (unsigned short) 0;\r
9420   mysa.sin_port = htons(uport);\r
9421   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9422       == SOCKET_ERROR) {\r
9423     err = WSAGetLastError();\r
9424     WSACleanup();\r
9425     return err;\r
9426   }\r
9427 \r
9428   /* Resolve remote host name */\r
9429   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9430   if (!(hp = gethostbyname(host))) {\r
9431     unsigned int b0, b1, b2, b3;\r
9432 \r
9433     err = WSAGetLastError();\r
9434 \r
9435     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9436       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9437       hp->h_addrtype = AF_INET;\r
9438       hp->h_length = 4;\r
9439       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9440       hp->h_addr_list[0] = (char *) malloc(4);\r
9441       hp->h_addr_list[0][0] = (char) b0;\r
9442       hp->h_addr_list[0][1] = (char) b1;\r
9443       hp->h_addr_list[0][2] = (char) b2;\r
9444       hp->h_addr_list[0][3] = (char) b3;\r
9445     } else {\r
9446       WSACleanup();\r
9447       return err;\r
9448     }\r
9449   }\r
9450   sa.sin_family = hp->h_addrtype;\r
9451   uport = (unsigned short) atoi(port);\r
9452   sa.sin_port = htons(uport);\r
9453   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9454 \r
9455   /* Make connection */\r
9456   if (connect(s, (struct sockaddr *) &sa,\r
9457               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9458     err = WSAGetLastError();\r
9459     WSACleanup();\r
9460     return err;\r
9461   }\r
9462 \r
9463   /* Prepare return value */\r
9464   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9465   cp->kind = CPSock;\r
9466   cp->sock = s;\r
9467   *pr = (ProcRef *) cp;\r
9468 \r
9469   return NO_ERROR;\r
9470 }\r
9471 \r
9472 int\r
9473 OpenCommPort(char *name, ProcRef *pr)\r
9474 {\r
9475   HANDLE h;\r
9476   COMMTIMEOUTS ct;\r
9477   ChildProc *cp;\r
9478   char fullname[MSG_SIZ];\r
9479 \r
9480   if (*name != '\\')\r
9481     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9482   else\r
9483     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9484 \r
9485   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9486                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9487   if (h == (HANDLE) -1) {\r
9488     return GetLastError();\r
9489   }\r
9490   hCommPort = h;\r
9491 \r
9492   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9493 \r
9494   /* Accumulate characters until a 100ms pause, then parse */\r
9495   ct.ReadIntervalTimeout = 100;\r
9496   ct.ReadTotalTimeoutMultiplier = 0;\r
9497   ct.ReadTotalTimeoutConstant = 0;\r
9498   ct.WriteTotalTimeoutMultiplier = 0;\r
9499   ct.WriteTotalTimeoutConstant = 0;\r
9500   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9501 \r
9502   /* Prepare return value */\r
9503   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9504   cp->kind = CPComm;\r
9505   cp->hFrom = h;\r
9506   cp->hTo = h;\r
9507   *pr = (ProcRef *) cp;\r
9508 \r
9509   return NO_ERROR;\r
9510 }\r
9511 \r
9512 int\r
9513 OpenLoopback(ProcRef *pr)\r
9514 {\r
9515   DisplayFatalError(_("Not implemented"), 0, 1);\r
9516   return NO_ERROR;\r
9517 }\r
9518 \r
9519 \r
9520 int\r
9521 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9522 {\r
9523   ChildProc *cp;\r
9524   int err;\r
9525   SOCKET s, s2, s3;\r
9526   struct sockaddr_in sa, mysa;\r
9527   struct hostent FAR *hp;\r
9528   unsigned short uport;\r
9529   WORD wVersionRequested;\r
9530   WSADATA wsaData;\r
9531   int fromPort;\r
9532   char stderrPortStr[MSG_SIZ];\r
9533 \r
9534   /* Initialize socket DLL */\r
9535   wVersionRequested = MAKEWORD(1, 1);\r
9536   err = WSAStartup(wVersionRequested, &wsaData);\r
9537   if (err != 0) return err;\r
9538 \r
9539   /* Resolve remote host name */\r
9540   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9541   if (!(hp = gethostbyname(host))) {\r
9542     unsigned int b0, b1, b2, b3;\r
9543 \r
9544     err = WSAGetLastError();\r
9545 \r
9546     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9547       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9548       hp->h_addrtype = AF_INET;\r
9549       hp->h_length = 4;\r
9550       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9551       hp->h_addr_list[0] = (char *) malloc(4);\r
9552       hp->h_addr_list[0][0] = (char) b0;\r
9553       hp->h_addr_list[0][1] = (char) b1;\r
9554       hp->h_addr_list[0][2] = (char) b2;\r
9555       hp->h_addr_list[0][3] = (char) b3;\r
9556     } else {\r
9557       WSACleanup();\r
9558       return err;\r
9559     }\r
9560   }\r
9561   sa.sin_family = hp->h_addrtype;\r
9562   uport = (unsigned short) 514;\r
9563   sa.sin_port = htons(uport);\r
9564   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9565 \r
9566   /* Bind local socket to unused "privileged" port address\r
9567    */\r
9568   s = INVALID_SOCKET;\r
9569   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9570   mysa.sin_family = AF_INET;\r
9571   mysa.sin_addr.s_addr = INADDR_ANY;\r
9572   for (fromPort = 1023;; fromPort--) {\r
9573     if (fromPort < 0) {\r
9574       WSACleanup();\r
9575       return WSAEADDRINUSE;\r
9576     }\r
9577     if (s == INVALID_SOCKET) {\r
9578       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9579         err = WSAGetLastError();\r
9580         WSACleanup();\r
9581         return err;\r
9582       }\r
9583     }\r
9584     uport = (unsigned short) fromPort;\r
9585     mysa.sin_port = htons(uport);\r
9586     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9587         == SOCKET_ERROR) {\r
9588       err = WSAGetLastError();\r
9589       if (err == WSAEADDRINUSE) continue;\r
9590       WSACleanup();\r
9591       return err;\r
9592     }\r
9593     if (connect(s, (struct sockaddr *) &sa,\r
9594       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9595       err = WSAGetLastError();\r
9596       if (err == WSAEADDRINUSE) {\r
9597         closesocket(s);\r
9598         s = -1;\r
9599         continue;\r
9600       }\r
9601       WSACleanup();\r
9602       return err;\r
9603     }\r
9604     break;\r
9605   }\r
9606 \r
9607   /* Bind stderr local socket to unused "privileged" port address\r
9608    */\r
9609   s2 = INVALID_SOCKET;\r
9610   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9611   mysa.sin_family = AF_INET;\r
9612   mysa.sin_addr.s_addr = INADDR_ANY;\r
9613   for (fromPort = 1023;; fromPort--) {\r
9614     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9615     if (fromPort < 0) {\r
9616       (void) closesocket(s);\r
9617       WSACleanup();\r
9618       return WSAEADDRINUSE;\r
9619     }\r
9620     if (s2 == INVALID_SOCKET) {\r
9621       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9622         err = WSAGetLastError();\r
9623         closesocket(s);\r
9624         WSACleanup();\r
9625         return err;\r
9626       }\r
9627     }\r
9628     uport = (unsigned short) fromPort;\r
9629     mysa.sin_port = htons(uport);\r
9630     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9631         == SOCKET_ERROR) {\r
9632       err = WSAGetLastError();\r
9633       if (err == WSAEADDRINUSE) continue;\r
9634       (void) closesocket(s);\r
9635       WSACleanup();\r
9636       return err;\r
9637     }\r
9638     if (listen(s2, 1) == SOCKET_ERROR) {\r
9639       err = WSAGetLastError();\r
9640       if (err == WSAEADDRINUSE) {\r
9641         closesocket(s2);\r
9642         s2 = INVALID_SOCKET;\r
9643         continue;\r
9644       }\r
9645       (void) closesocket(s);\r
9646       (void) closesocket(s2);\r
9647       WSACleanup();\r
9648       return err;\r
9649     }\r
9650     break;\r
9651   }\r
9652   prevStderrPort = fromPort; // remember port used\r
9653   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9654 \r
9655   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9656     err = WSAGetLastError();\r
9657     (void) closesocket(s);\r
9658     (void) closesocket(s2);\r
9659     WSACleanup();\r
9660     return err;\r
9661   }\r
9662 \r
9663   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9664     err = WSAGetLastError();\r
9665     (void) closesocket(s);\r
9666     (void) closesocket(s2);\r
9667     WSACleanup();\r
9668     return err;\r
9669   }\r
9670   if (*user == NULLCHAR) user = UserName();\r
9671   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9672     err = WSAGetLastError();\r
9673     (void) closesocket(s);\r
9674     (void) closesocket(s2);\r
9675     WSACleanup();\r
9676     return err;\r
9677   }\r
9678   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9679     err = WSAGetLastError();\r
9680     (void) closesocket(s);\r
9681     (void) closesocket(s2);\r
9682     WSACleanup();\r
9683     return err;\r
9684   }\r
9685 \r
9686   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9687     err = WSAGetLastError();\r
9688     (void) closesocket(s);\r
9689     (void) closesocket(s2);\r
9690     WSACleanup();\r
9691     return err;\r
9692   }\r
9693   (void) closesocket(s2);  /* Stop listening */\r
9694 \r
9695   /* Prepare return value */\r
9696   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9697   cp->kind = CPRcmd;\r
9698   cp->sock = s;\r
9699   cp->sock2 = s3;\r
9700   *pr = (ProcRef *) cp;\r
9701 \r
9702   return NO_ERROR;\r
9703 }\r
9704 \r
9705 \r
9706 InputSourceRef\r
9707 AddInputSource(ProcRef pr, int lineByLine,\r
9708                InputCallback func, VOIDSTAR closure)\r
9709 {\r
9710   InputSource *is, *is2 = NULL;\r
9711   ChildProc *cp = (ChildProc *) pr;\r
9712 \r
9713   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9714   is->lineByLine = lineByLine;\r
9715   is->func = func;\r
9716   is->closure = closure;\r
9717   is->second = NULL;\r
9718   is->next = is->buf;\r
9719   if (pr == NoProc) {\r
9720     is->kind = CPReal;\r
9721     consoleInputSource = is;\r
9722   } else {\r
9723     is->kind = cp->kind;\r
9724     /* \r
9725         [AS] Try to avoid a race condition if the thread is given control too early:\r
9726         we create all threads suspended so that the is->hThread variable can be\r
9727         safely assigned, then let the threads start with ResumeThread.\r
9728     */\r
9729     switch (cp->kind) {\r
9730     case CPReal:\r
9731       is->hFile = cp->hFrom;\r
9732       cp->hFrom = NULL; /* now owned by InputThread */\r
9733       is->hThread =\r
9734         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9735                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9736       break;\r
9737 \r
9738     case CPComm:\r
9739       is->hFile = cp->hFrom;\r
9740       cp->hFrom = NULL; /* now owned by InputThread */\r
9741       is->hThread =\r
9742         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9743                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9744       break;\r
9745 \r
9746     case CPSock:\r
9747       is->sock = cp->sock;\r
9748       is->hThread =\r
9749         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9750                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9751       break;\r
9752 \r
9753     case CPRcmd:\r
9754       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9755       *is2 = *is;\r
9756       is->sock = cp->sock;\r
9757       is->second = is2;\r
9758       is2->sock = cp->sock2;\r
9759       is2->second = is2;\r
9760       is->hThread =\r
9761         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9762                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9763       is2->hThread =\r
9764         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9765                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9766       break;\r
9767     }\r
9768 \r
9769     if( is->hThread != NULL ) {\r
9770         ResumeThread( is->hThread );\r
9771     }\r
9772 \r
9773     if( is2 != NULL && is2->hThread != NULL ) {\r
9774         ResumeThread( is2->hThread );\r
9775     }\r
9776   }\r
9777 \r
9778   return (InputSourceRef) is;\r
9779 }\r
9780 \r
9781 void\r
9782 RemoveInputSource(InputSourceRef isr)\r
9783 {\r
9784   InputSource *is;\r
9785 \r
9786   is = (InputSource *) isr;\r
9787   is->hThread = NULL;  /* tell thread to stop */\r
9788   CloseHandle(is->hThread);\r
9789   if (is->second != NULL) {\r
9790     is->second->hThread = NULL;\r
9791     CloseHandle(is->second->hThread);\r
9792   }\r
9793 }\r
9794 \r
9795 int no_wrap(char *message, int count)\r
9796 {\r
9797     ConsoleOutput(message, count, FALSE);\r
9798     return count;\r
9799 }\r
9800 \r
9801 int\r
9802 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9803 {\r
9804   DWORD dOutCount;\r
9805   int outCount = SOCKET_ERROR;\r
9806   ChildProc *cp = (ChildProc *) pr;\r
9807   static OVERLAPPED ovl;\r
9808 \r
9809   static int line = 0;\r
9810 \r
9811   if (pr == NoProc)\r
9812   {\r
9813     if (appData.noJoin || !appData.useInternalWrap)\r
9814       return no_wrap(message, count);\r
9815     else\r
9816     {\r
9817       int width = get_term_width();\r
9818       int len = wrap(NULL, message, count, width, &line);\r
9819       char *msg = malloc(len);\r
9820       int dbgchk;\r
9821 \r
9822       if (!msg)\r
9823         return no_wrap(message, count);\r
9824       else\r
9825       {\r
9826         dbgchk = wrap(msg, message, count, width, &line);\r
9827         if (dbgchk != len && appData.debugMode)\r
9828             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9829         ConsoleOutput(msg, len, FALSE);\r
9830         free(msg);\r
9831         return len;\r
9832       }\r
9833     }\r
9834   }\r
9835 \r
9836   if (ovl.hEvent == NULL) {\r
9837     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9838   }\r
9839   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9840 \r
9841   switch (cp->kind) {\r
9842   case CPSock:\r
9843   case CPRcmd:\r
9844     outCount = send(cp->sock, message, count, 0);\r
9845     if (outCount == SOCKET_ERROR) {\r
9846       *outError = WSAGetLastError();\r
9847     } else {\r
9848       *outError = NO_ERROR;\r
9849     }\r
9850     break;\r
9851 \r
9852   case CPReal:\r
9853     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9854                   &dOutCount, NULL)) {\r
9855       *outError = NO_ERROR;\r
9856       outCount = (int) dOutCount;\r
9857     } else {\r
9858       *outError = GetLastError();\r
9859     }\r
9860     break;\r
9861 \r
9862   case CPComm:\r
9863     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9864                             &dOutCount, &ovl);\r
9865     if (*outError == NO_ERROR) {\r
9866       outCount = (int) dOutCount;\r
9867     }\r
9868     break;\r
9869   }\r
9870   return outCount;\r
9871 }\r
9872 \r
9873 void\r
9874 DoSleep(int n)\r
9875 {\r
9876     if(n != 0) Sleep(n);\r
9877 }\r
9878 \r
9879 int\r
9880 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9881                        long msdelay)\r
9882 {\r
9883   /* Ignore delay, not implemented for WinBoard */\r
9884   return OutputToProcess(pr, message, count, outError);\r
9885 }\r
9886 \r
9887 \r
9888 void\r
9889 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9890                         char *buf, int count, int error)\r
9891 {\r
9892   DisplayFatalError(_("Not implemented"), 0, 1);\r
9893 }\r
9894 \r
9895 /* see wgamelist.c for Game List functions */\r
9896 /* see wedittags.c for Edit Tags functions */\r
9897 \r
9898 \r
9899 int\r
9900 ICSInitScript()\r
9901 {\r
9902   FILE *f;\r
9903   char buf[MSG_SIZ];\r
9904   char *dummy;\r
9905 \r
9906   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9907     f = fopen(buf, "r");\r
9908     if (f != NULL) {\r
9909       ProcessICSInitScript(f);\r
9910       fclose(f);\r
9911       return TRUE;\r
9912     }\r
9913   }\r
9914   return FALSE;\r
9915 }\r
9916 \r
9917 \r
9918 VOID\r
9919 StartAnalysisClock()\r
9920 {\r
9921   if (analysisTimerEvent) return;\r
9922   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9923                                         (UINT) 2000, NULL);\r
9924 }\r
9925 \r
9926 VOID\r
9927 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9928 {\r
9929   highlightInfo.sq[0].x = fromX;\r
9930   highlightInfo.sq[0].y = fromY;\r
9931   highlightInfo.sq[1].x = toX;\r
9932   highlightInfo.sq[1].y = toY;\r
9933 }\r
9934 \r
9935 VOID\r
9936 ClearHighlights()\r
9937 {\r
9938   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9939     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9940 }\r
9941 \r
9942 VOID\r
9943 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9944 {\r
9945   premoveHighlightInfo.sq[0].x = fromX;\r
9946   premoveHighlightInfo.sq[0].y = fromY;\r
9947   premoveHighlightInfo.sq[1].x = toX;\r
9948   premoveHighlightInfo.sq[1].y = toY;\r
9949 }\r
9950 \r
9951 VOID\r
9952 ClearPremoveHighlights()\r
9953 {\r
9954   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9955     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9956 }\r
9957 \r
9958 VOID\r
9959 ShutDownFrontEnd()\r
9960 {\r
9961   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9962   DeleteClipboardTempFiles();\r
9963 }\r
9964 \r
9965 void\r
9966 BoardToTop()\r
9967 {\r
9968     if (IsIconic(hwndMain))\r
9969       ShowWindow(hwndMain, SW_RESTORE);\r
9970 \r
9971     SetActiveWindow(hwndMain);\r
9972 }\r
9973 \r
9974 /*\r
9975  * Prototypes for animation support routines\r
9976  */\r
9977 static void ScreenSquare(int column, int row, POINT * pt);\r
9978 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9979      POINT frames[], int * nFrames);\r
9980 \r
9981 \r
9982 #define kFactor 4\r
9983 \r
9984 void\r
9985 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9986 {       // [HGM] atomic: animate blast wave\r
9987         int i;\r
9988 \r
9989         explodeInfo.fromX = fromX;\r
9990         explodeInfo.fromY = fromY;\r
9991         explodeInfo.toX = toX;\r
9992         explodeInfo.toY = toY;\r
9993         for(i=1; i<4*kFactor; i++) {\r
9994             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9995             DrawPosition(FALSE, board);\r
9996             Sleep(appData.animSpeed);\r
9997         }\r
9998         explodeInfo.radius = 0;\r
9999         DrawPosition(TRUE, board);\r
10000 }\r
10001 \r
10002 void\r
10003 AnimateMove(board, fromX, fromY, toX, toY)\r
10004      Board board;\r
10005      int fromX;\r
10006      int fromY;\r
10007      int toX;\r
10008      int toY;\r
10009 {\r
10010   ChessSquare piece;\r
10011   int x = toX, y = toY;\r
10012   POINT start, finish, mid;\r
10013   POINT frames[kFactor * 2 + 1];\r
10014   int nFrames, n;\r
10015 \r
10016   if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();\r
10017 \r
10018   if (!appData.animate) return;\r
10019   if (doingSizing) return;\r
10020   if (fromY < 0 || fromX < 0) return;\r
10021   piece = board[fromY][fromX];\r
10022   if (piece >= EmptySquare) return;\r
10023 \r
10024   if(killX >= 0) toX = killX, toY = killY; // [HGM] lion: first to kill square\r
10025 \r
10026 again:\r
10027 \r
10028   ScreenSquare(fromX, fromY, &start);\r
10029   ScreenSquare(toX, toY, &finish);\r
10030 \r
10031   /* All moves except knight jumps move in straight line */\r
10032   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
10033     mid.x = start.x + (finish.x - start.x) / 2;\r
10034     mid.y = start.y + (finish.y - start.y) / 2;\r
10035   } else {\r
10036     /* Knight: make straight movement then diagonal */\r
10037     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10038        mid.x = start.x + (finish.x - start.x) / 2;\r
10039        mid.y = start.y;\r
10040      } else {\r
10041        mid.x = start.x;\r
10042        mid.y = start.y + (finish.y - start.y) / 2;\r
10043      }\r
10044   }\r
10045   \r
10046   /* Don't use as many frames for very short moves */\r
10047   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10048     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10049   else\r
10050     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10051 \r
10052   animInfo.from.x = fromX;\r
10053   animInfo.from.y = fromY;\r
10054   animInfo.to.x = toX;\r
10055   animInfo.to.y = toY;\r
10056   animInfo.lastpos = start;\r
10057   animInfo.piece = piece;\r
10058   for (n = 0; n < nFrames; n++) {\r
10059     animInfo.pos = frames[n];\r
10060     DrawPosition(FALSE, NULL);\r
10061     animInfo.lastpos = animInfo.pos;\r
10062     Sleep(appData.animSpeed);\r
10063   }\r
10064   animInfo.pos = finish;\r
10065   DrawPosition(FALSE, NULL);\r
10066 \r
10067   if(toX != x || toY != y) { fromX = toX; fromY = toY; toX = x; toY = y; goto again; } // second leg\r
10068 \r
10069   animInfo.piece = EmptySquare;\r
10070   Explode(board, fromX, fromY, toX, toY);\r
10071 }\r
10072 \r
10073 /*      Convert board position to corner of screen rect and color       */\r
10074 \r
10075 static void\r
10076 ScreenSquare(column, row, pt)\r
10077      int column; int row; POINT * pt;\r
10078 {\r
10079   if (flipView) {\r
10080     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
10081     pt->y = lineGap + row * (squareSize + lineGap) + border;\r
10082   } else {\r
10083     pt->x = lineGap + column * (squareSize + lineGap) + border;\r
10084     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
10085   }\r
10086 }\r
10087 \r
10088 /*      Generate a series of frame coords from start->mid->finish.\r
10089         The movement rate doubles until the half way point is\r
10090         reached, then halves back down to the final destination,\r
10091         which gives a nice slow in/out effect. The algorithmn\r
10092         may seem to generate too many intermediates for short\r
10093         moves, but remember that the purpose is to attract the\r
10094         viewers attention to the piece about to be moved and\r
10095         then to where it ends up. Too few frames would be less\r
10096         noticeable.                                             */\r
10097 \r
10098 static void\r
10099 Tween(start, mid, finish, factor, frames, nFrames)\r
10100      POINT * start; POINT * mid;\r
10101      POINT * finish; int factor;\r
10102      POINT frames[]; int * nFrames;\r
10103 {\r
10104   int n, fraction = 1, count = 0;\r
10105 \r
10106   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10107   for (n = 0; n < factor; n++)\r
10108     fraction *= 2;\r
10109   for (n = 0; n < factor; n++) {\r
10110     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10111     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10112     count ++;\r
10113     fraction = fraction / 2;\r
10114   }\r
10115   \r
10116   /* Midpoint */\r
10117   frames[count] = *mid;\r
10118   count ++;\r
10119   \r
10120   /* Slow out, stepping 1/2, then 1/4, ... */\r
10121   fraction = 2;\r
10122   for (n = 0; n < factor; n++) {\r
10123     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10124     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10125     count ++;\r
10126     fraction = fraction * 2;\r
10127   }\r
10128   *nFrames = count;\r
10129 }\r
10130 \r
10131 void\r
10132 SettingsPopUp(ChessProgramState *cps)\r
10133 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
10134       EngineOptionsPopup(savedHwnd, cps);\r
10135 }\r
10136 \r
10137 int flock(int fid, int code)\r
10138 {\r
10139     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
10140     OVERLAPPED ov;\r
10141     ov.hEvent = NULL;\r
10142     ov.Offset = 0;\r
10143     ov.OffsetHigh = 0;\r
10144     switch(code) {\r
10145       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
10146 \r
10147       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
10148       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
10149       default: return -1;\r
10150     }\r
10151     return 0;\r
10152 }\r
10153 \r
10154 char *\r
10155 Col2Text (int n)\r
10156 {\r
10157     static int i=0;\r
10158     static char col[8][20];\r
10159     COLORREF color = *(COLORREF *) colorVariable[n];\r
10160     i = i+1 & 7;\r
10161     snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
10162     return col[i];\r
10163 }\r
10164 \r
10165 void\r
10166 ActivateTheme (int new)\r
10167 {   // Redo initialization of features depending on options that can occur in themes\r
10168    InitTextures();\r
10169    if(new) InitDrawingColors();\r
10170    fontBitmapSquareSize = 0; // request creation of new font pieces\r
10171    InitDrawingSizes(boardSize, 0);\r
10172    InvalidateRect(hwndMain, NULL, TRUE);\r
10173 }\r