Adapt WinBoard front-end to Mighty Lion
[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 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 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
96 \r
97   int myrandom(void);\r
98   void mysrandom(unsigned int seed);\r
99 \r
100 extern int whiteFlag, blackFlag;\r
101 Boolean flipClock = FALSE;\r
102 extern HANDLE chatHandle[];\r
103 extern enum ICS_TYPE ics_type;\r
104 \r
105 int  MySearchPath P((char *installDir, char *name, char *fullname));\r
106 int  MyGetFullPathName P((char *name, char *fullname));\r
107 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
108 VOID NewVariantPopup(HWND hwnd);\r
109 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
110                    /*char*/int promoChar));\r
111 void DisplayMove P((int moveNumber));\r
112 void ChatPopUp P((char *s));\r
113 typedef struct {\r
114   ChessSquare piece;  \r
115   POINT pos;      /* window coordinates of current pos */\r
116   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
117   POINT from;     /* board coordinates of the piece's orig pos */\r
118   POINT to;       /* board coordinates of the piece's new pos */\r
119 } AnimInfo;\r
120 \r
121 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
122 \r
123 typedef struct {\r
124   POINT start;    /* window coordinates of start pos */\r
125   POINT pos;      /* window coordinates of current pos */\r
126   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
127   POINT from;     /* board coordinates of the piece's orig pos */\r
128   ChessSquare piece;\r
129 } DragInfo;\r
130 \r
131 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
132 \r
133 typedef struct {\r
134   POINT sq[2];    /* board coordinates of from, to squares */\r
135 } HighlightInfo;\r
136 \r
137 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
138 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
139 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
140 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
141 \r
142 typedef struct { // [HGM] atomic\r
143   int fromX, fromY, toX, toY, radius;\r
144 } ExplodeInfo;\r
145 \r
146 static ExplodeInfo explodeInfo;\r
147 \r
148 /* Window class names */\r
149 char szAppName[] = "WinBoard";\r
150 char szConsoleName[] = "WBConsole";\r
151 \r
152 /* Title bar text */\r
153 char szTitle[] = "WinBoard";\r
154 char szConsoleTitle[] = "I C S Interaction";\r
155 \r
156 char *programName;\r
157 char *settingsFileName;\r
158 Boolean saveSettingsOnExit;\r
159 char installDir[MSG_SIZ];\r
160 int errorExitStatus;\r
161 \r
162 BoardSize boardSize;\r
163 Boolean chessProgram;\r
164 //static int boardX, boardY;\r
165 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
166 int squareSize, lineGap, minorSize, border;\r
167 static int winW, winH;\r
168 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
169 static int logoHeight = 0;\r
170 static char messageText[MESSAGE_TEXT_MAX];\r
171 static int clockTimerEvent = 0;\r
172 static int loadGameTimerEvent = 0;\r
173 static int analysisTimerEvent = 0;\r
174 static DelayedEventCallback delayedTimerCallback;\r
175 static int delayedTimerEvent = 0;\r
176 static int buttonCount = 2;\r
177 char *icsTextMenuString;\r
178 char *icsNames;\r
179 char *firstChessProgramNames;\r
180 char *secondChessProgramNames;\r
181 \r
182 #define PALETTESIZE 256\r
183 \r
184 HINSTANCE hInst;          /* current instance */\r
185 Boolean alwaysOnTop = FALSE;\r
186 RECT boardRect;\r
187 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
188   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
189 COLORREF markerColor[8] = { 0x00FFFF, 0x0000FF, 0x00FF00, 0xFF0000, 0xFFFF00, 0xFF00FF, 0xFFFFFF, 0x000000 };\r
190 HPALETTE hPal;\r
191 ColorClass currentColorClass;\r
192 \r
193 static HWND savedHwnd;\r
194 HWND hCommPort = NULL;    /* currently open comm port */\r
195 static HWND hwndPause;    /* pause button */\r
196 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
197 static HBRUSH lightSquareBrush, darkSquareBrush,\r
198   blackSquareBrush, /* [HGM] for band between board and holdings */\r
199   explodeBrush,     /* [HGM] atomic */\r
200   markerBrush[8],   /* [HGM] markers */\r
201   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
202 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
203 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
204 static HPEN gridPen = NULL;\r
205 static HPEN highlightPen = NULL;\r
206 static HPEN premovePen = NULL;\r
207 static NPLOGPALETTE pLogPal;\r
208 static BOOL paletteChanged = FALSE;\r
209 static HICON iconWhite, iconBlack, iconCurrent;\r
210 static int doingSizing = FALSE;\r
211 static int lastSizing = 0;\r
212 static int prevStderrPort;\r
213 static HBITMAP userLogo;\r
214 \r
215 static HBITMAP liteBackTexture = NULL;\r
216 static HBITMAP darkBackTexture = NULL;\r
217 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
218 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
219 static int backTextureSquareSize = 0;\r
220 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
221 \r
222 #if __GNUC__ && !defined(_winmajor)\r
223 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
224 #else\r
225 \r
226 #if defined(_winmajor)\r
227 #define oldDialog (_winmajor < 4)\r
228 #else\r
229 #define oldDialog 0\r
230 #endif\r
231 #endif\r
232 \r
233 #define INTERNATIONAL\r
234 \r
235 #ifdef INTERNATIONAL\r
236 #  define _(s) T_(s)\r
237 #  define N_(s) s\r
238 #else\r
239 #  define _(s) s\r
240 #  define N_(s) s\r
241 #  define T_(s) s\r
242 #  define Translate(x, y)\r
243 #  define LoadLanguageFile(s)\r
244 #endif\r
245 \r
246 #ifdef INTERNATIONAL\r
247 \r
248 Boolean barbaric; // flag indicating if translation is needed\r
249 \r
250 // list of item numbers used in each dialog (used to alter language at run time)\r
251 \r
252 #define ABOUTBOX -1  /* not sure why these are needed */\r
253 #define ABOUTBOX2 -1\r
254 \r
255 int dialogItems[][42] = {\r
256 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
257 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
258   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
259 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,\r
260   OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL }, \r
261 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
262   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
263 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
264 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
265   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
266 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
267 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
268   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
269 { ABOUTBOX2, IDC_ChessBoard }, \r
270 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
271   OPT_GameListClose, IDC_GameListDoFilter }, \r
272 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
273 { DLG_Error, IDOK }, \r
274 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
275   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
276 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
277 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
278   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
279   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
280 { DLG_IndexNumber, IDC_Index }, \r
281 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
282 { DLG_TypeInName, IDOK, IDCANCEL }, \r
283 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
284   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
285 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
286   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
287   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
288   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
289   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
290   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
291   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
292 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
293   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
294   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
295   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
296   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
297   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
298   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
299   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
300   GPB_General, GPB_Alarm, OPT_AutoCreate }, \r
301 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
302   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
303   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
304   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
305   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
306   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
307   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
308   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
309 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
310   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
311   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
312   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
313   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
314   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
315   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
316   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
317   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
318 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
319   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
320   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
321   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
322   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
323 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
324 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
325   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
326 { DLG_MoveHistory }, \r
327 { DLG_EvalGraph }, \r
328 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
329 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
330 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
331   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
332   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
333   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
334 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
335   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
336   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
337 { 0 }\r
338 };\r
339 \r
340 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
341 static int lastChecked;\r
342 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
343 extern int tinyLayout;\r
344 extern char * menuBarText[][10];\r
345 \r
346 void\r
347 LoadLanguageFile(char *name)\r
348 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
349     FILE *f;\r
350     int i=0, j=0, n=0, k;\r
351     char buf[MSG_SIZ];\r
352 \r
353     if(!name || name[0] == NULLCHAR) return;\r
354       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
355     appData.language = oldLanguage;\r
356     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
357     if((f = fopen(buf, "r")) == NULL) return;\r
358     while((k = fgetc(f)) != EOF) {\r
359         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
360         languageBuf[i] = k;\r
361         if(k == '\n') {\r
362             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
363                 char *p;\r
364                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
365                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
366                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
367                         english[j] = languageBuf + n + 1; *p = 0;\r
368                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
369 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
370                     }\r
371                 }\r
372             }\r
373             n = i + 1;\r
374         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
375             switch(k) {\r
376               case 'n': k = '\n'; break;\r
377               case 'r': k = '\r'; break;\r
378               case 't': k = '\t'; break;\r
379             }\r
380             languageBuf[--i] = k;\r
381         }\r
382         i++;\r
383     }\r
384     fclose(f);\r
385     barbaric = (j != 0);\r
386     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
387 }\r
388 \r
389 char *\r
390 T_(char *s)\r
391 {   // return the translation of the given string\r
392     // efficiency can be improved a lot...\r
393     int i=0;\r
394     static char buf[MSG_SIZ];\r
395 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
396     if(!barbaric) return s;\r
397     if(!s) return ""; // sanity\r
398     while(english[i]) {\r
399         if(!strcmp(s, english[i])) return foreign[i];\r
400         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
401             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
402             return buf;\r
403         }\r
404         i++;\r
405     }\r
406     return s;\r
407 }\r
408 \r
409 void\r
410 Translate(HWND hDlg, int dialogID)\r
411 {   // translate all text items in the given dialog\r
412     int i=0, j, k;\r
413     char buf[MSG_SIZ], *s;\r
414     if(!barbaric) return;\r
415     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
416     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
417     GetWindowText( hDlg, buf, MSG_SIZ );\r
418     s = T_(buf);\r
419     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
420     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
421         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
422         if(strlen(buf) == 0) continue;\r
423         s = T_(buf);\r
424         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
425     }\r
426 }\r
427 \r
428 HMENU\r
429 TranslateOneMenu(int i, HMENU subMenu)\r
430 {\r
431     int j;\r
432     static MENUITEMINFO info;\r
433 \r
434     info.cbSize = sizeof(MENUITEMINFO);\r
435     info.fMask = MIIM_STATE | MIIM_TYPE;\r
436           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
437             char buf[MSG_SIZ];\r
438             info.dwTypeData = buf;\r
439             info.cch = sizeof(buf);\r
440             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
441             if(i < 10) {\r
442                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
443                 else menuText[i][j] = strdup(buf); // remember original on first change\r
444             }\r
445             if(buf[0] == NULLCHAR) continue;\r
446             info.dwTypeData = T_(buf);\r
447             info.cch = strlen(buf)+1;\r
448             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
449           }\r
450     return subMenu;\r
451 }\r
452 \r
453 void\r
454 TranslateMenus(int addLanguage)\r
455 {\r
456     int i;\r
457     WIN32_FIND_DATA fileData;\r
458     HANDLE hFind;\r
459 #define IDM_English 1970\r
460     if(1) {\r
461         HMENU mainMenu = GetMenu(hwndMain);\r
462         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
463           HMENU subMenu = GetSubMenu(mainMenu, i);\r
464           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
465                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
466           TranslateOneMenu(i, subMenu);\r
467         }\r
468         DrawMenuBar(hwndMain);\r
469     }\r
470 \r
471     if(!addLanguage) return;\r
472     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
473         HMENU mainMenu = GetMenu(hwndMain);\r
474         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
475         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
476         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
477         i = 0; lastChecked = IDM_English;\r
478         do {\r
479             char *p, *q = fileData.cFileName;\r
480             int checkFlag = MF_UNCHECKED;\r
481             languageFile[i] = strdup(q);\r
482             if(barbaric && !strcmp(oldLanguage, q)) {\r
483                 checkFlag = MF_CHECKED;\r
484                 lastChecked = IDM_English + i + 1;\r
485                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
486             }\r
487             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
488             p = strstr(fileData.cFileName, ".lng");\r
489             if(p) *p = 0;\r
490             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
491         } while(FindNextFile(hFind, &fileData));\r
492         FindClose(hFind);\r
493     }\r
494 }\r
495 \r
496 #endif\r
497 \r
498 #define IDM_RecentEngines 3000\r
499 \r
500 void\r
501 RecentEngineMenu (char *s)\r
502 {\r
503     if(appData.icsActive) return;\r
504     if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
505         HMENU mainMenu = GetMenu(hwndMain);\r
506         HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
507         int i=IDM_RecentEngines;\r
508         recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
509         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
510         while(*s) {\r
511           char *p = strchr(s, '\n');\r
512           if(p == NULL) return; // malformed!\r
513           *p = NULLCHAR;\r
514           AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
515           *p = '\n';\r
516           s = p+1;\r
517         }\r
518     }\r
519 }\r
520 \r
521 \r
522 typedef struct {\r
523   char *name;\r
524   int squareSize;\r
525   int lineGap;\r
526   int smallLayout;\r
527   int tinyLayout;\r
528   int cliWidth, cliHeight;\r
529 } SizeInfo;\r
530 \r
531 SizeInfo sizeInfo[] = \r
532 {\r
533   { "tiny",     21, 0, 1, 1, 0, 0 },\r
534   { "teeny",    25, 1, 1, 1, 0, 0 },\r
535   { "dinky",    29, 1, 1, 1, 0, 0 },\r
536   { "petite",   33, 1, 1, 1, 0, 0 },\r
537   { "slim",     37, 2, 1, 0, 0, 0 },\r
538   { "small",    40, 2, 1, 0, 0, 0 },\r
539   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
540   { "middling", 49, 2, 0, 0, 0, 0 },\r
541   { "average",  54, 2, 0, 0, 0, 0 },\r
542   { "moderate", 58, 3, 0, 0, 0, 0 },\r
543   { "medium",   64, 3, 0, 0, 0, 0 },\r
544   { "bulky",    72, 3, 0, 0, 0, 0 },\r
545   { "large",    80, 3, 0, 0, 0, 0 },\r
546   { "big",      87, 3, 0, 0, 0, 0 },\r
547   { "huge",     95, 3, 0, 0, 0, 0 },\r
548   { "giant",    108, 3, 0, 0, 0, 0 },\r
549   { "colossal", 116, 4, 0, 0, 0, 0 },\r
550   { "titanic",  129, 4, 0, 0, 0, 0 },\r
551   { NULL, 0, 0, 0, 0, 0, 0 }\r
552 };\r
553 \r
554 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
555 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
556 {\r
557   { 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
558   { 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
559   { 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
560   { 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
561   { 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
562   { 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
563   { 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
564   { 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
565   { 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
566   { 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
567   { 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
568   { 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
569   { 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
570   { 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
571   { 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
572   { 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
573   { 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
574   { 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
575 };\r
576 \r
577 MyFont *font[NUM_SIZES][NUM_FONTS];\r
578 \r
579 typedef struct {\r
580   char *label;\r
581   int id;\r
582   HWND hwnd;\r
583   WNDPROC wndproc;\r
584 } MyButtonDesc;\r
585 \r
586 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
587 #define N_BUTTONS 5\r
588 \r
589 MyButtonDesc buttonDesc[N_BUTTONS] =\r
590 {\r
591   {"<<", IDM_ToStart, NULL, NULL},\r
592   {"<", IDM_Backward, NULL, NULL},\r
593   {"P", IDM_Pause, NULL, NULL},\r
594   {">", IDM_Forward, NULL, NULL},\r
595   {">>", IDM_ToEnd, NULL, NULL},\r
596 };\r
597 \r
598 int tinyLayout = 0, smallLayout = 0;\r
599 #define MENU_BAR_ITEMS 9\r
600 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
601   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
602   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
603 };\r
604 \r
605 \r
606 MySound sounds[(int)NSoundClasses];\r
607 MyTextAttribs textAttribs[(int)NColorClasses];\r
608 \r
609 MyColorizeAttribs colorizeAttribs[] = {\r
610   { (COLORREF)0, 0, N_("Shout Text") },\r
611   { (COLORREF)0, 0, N_("SShout/CShout") },\r
612   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
613   { (COLORREF)0, 0, N_("Channel Text") },\r
614   { (COLORREF)0, 0, N_("Kibitz Text") },\r
615   { (COLORREF)0, 0, N_("Tell Text") },\r
616   { (COLORREF)0, 0, N_("Challenge Text") },\r
617   { (COLORREF)0, 0, N_("Request Text") },\r
618   { (COLORREF)0, 0, N_("Seek Text") },\r
619   { (COLORREF)0, 0, N_("Normal Text") },\r
620   { (COLORREF)0, 0, N_("None") }\r
621 };\r
622 \r
623 \r
624 \r
625 static char *commentTitle;\r
626 static char *commentText;\r
627 static int commentIndex;\r
628 static Boolean editComment = FALSE;\r
629 \r
630 \r
631 char errorTitle[MSG_SIZ];\r
632 char errorMessage[2*MSG_SIZ];\r
633 HWND errorDialog = NULL;\r
634 BOOLEAN moveErrorMessageUp = FALSE;\r
635 BOOLEAN consoleEcho = TRUE;\r
636 CHARFORMAT consoleCF;\r
637 COLORREF consoleBackgroundColor;\r
638 \r
639 char *programVersion;\r
640 \r
641 #define CPReal 1\r
642 #define CPComm 2\r
643 #define CPSock 3\r
644 #define CPRcmd 4\r
645 typedef int CPKind;\r
646 \r
647 typedef struct {\r
648   CPKind kind;\r
649   HANDLE hProcess;\r
650   DWORD pid;\r
651   HANDLE hTo;\r
652   HANDLE hFrom;\r
653   SOCKET sock;\r
654   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
655 } ChildProc;\r
656 \r
657 #define INPUT_SOURCE_BUF_SIZE 4096\r
658 \r
659 typedef struct _InputSource {\r
660   CPKind kind;\r
661   HANDLE hFile;\r
662   SOCKET sock;\r
663   int lineByLine;\r
664   HANDLE hThread;\r
665   DWORD id;\r
666   char buf[INPUT_SOURCE_BUF_SIZE];\r
667   char *next;\r
668   DWORD count;\r
669   int error;\r
670   InputCallback func;\r
671   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
672   VOIDSTAR closure;\r
673 } InputSource;\r
674 \r
675 InputSource *consoleInputSource;\r
676 \r
677 DCB dcb;\r
678 \r
679 /* forward */\r
680 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
681 VOID ConsoleCreate();\r
682 LRESULT CALLBACK\r
683   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
684 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
685 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
686 VOID ParseCommSettings(char *arg, DCB *dcb);\r
687 LRESULT CALLBACK\r
688   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
689 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
690 void ParseIcsTextMenu(char *icsTextMenuString);\r
691 VOID PopUpNameDialog(char firstchar);\r
692 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
693 \r
694 /* [AS] */\r
695 int NewGameFRC();\r
696 int GameListOptions();\r
697 \r
698 int dummy; // [HGM] for obsolete args\r
699 \r
700 HWND hwndMain = NULL;        /* root window*/\r
701 HWND hwndConsole = NULL;\r
702 HWND commentDialog = NULL;\r
703 HWND moveHistoryDialog = NULL;\r
704 HWND evalGraphDialog = NULL;\r
705 HWND engineOutputDialog = NULL;\r
706 HWND gameListDialog = NULL;\r
707 HWND editTagsDialog = NULL;\r
708 \r
709 int commentUp = FALSE;\r
710 \r
711 WindowPlacement wpMain;\r
712 WindowPlacement wpConsole;\r
713 WindowPlacement wpComment;\r
714 WindowPlacement wpMoveHistory;\r
715 WindowPlacement wpEvalGraph;\r
716 WindowPlacement wpEngineOutput;\r
717 WindowPlacement wpGameList;\r
718 WindowPlacement wpTags;\r
719 \r
720 VOID EngineOptionsPopup(); // [HGM] settings\r
721 \r
722 VOID GothicPopUp(char *title, VariantClass variant);\r
723 /*\r
724  * Setting "frozen" should disable all user input other than deleting\r
725  * the window.  We do this while engines are initializing themselves.\r
726  */\r
727 static int frozen = 0;\r
728 static int oldMenuItemState[MENU_BAR_ITEMS];\r
729 void FreezeUI()\r
730 {\r
731   HMENU hmenu;\r
732   int i;\r
733 \r
734   if (frozen) return;\r
735   frozen = 1;\r
736   hmenu = GetMenu(hwndMain);\r
737   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
738     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
739   }\r
740   DrawMenuBar(hwndMain);\r
741 }\r
742 \r
743 /* Undo a FreezeUI */\r
744 void ThawUI()\r
745 {\r
746   HMENU hmenu;\r
747   int i;\r
748 \r
749   if (!frozen) return;\r
750   frozen = 0;\r
751   hmenu = GetMenu(hwndMain);\r
752   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
753     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
754   }\r
755   DrawMenuBar(hwndMain);\r
756 }\r
757 \r
758 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
759 \r
760 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
761 #ifdef JAWS\r
762 #include "jaws.c"\r
763 #else\r
764 #define JAWS_INIT\r
765 #define JAWS_ARGS\r
766 #define JAWS_ALT_INTERCEPT\r
767 #define JAWS_KBUP_NAVIGATION\r
768 #define JAWS_KBDOWN_NAVIGATION\r
769 #define JAWS_MENU_ITEMS\r
770 #define JAWS_SILENCE\r
771 #define JAWS_REPLAY\r
772 #define JAWS_ACCEL\r
773 #define JAWS_COPYRIGHT\r
774 #define JAWS_DELETE(X) X\r
775 #define SAYMACHINEMOVE()\r
776 #define SAY(X)\r
777 #endif\r
778 \r
779 /*---------------------------------------------------------------------------*\\r
780  *\r
781  * WinMain\r
782  *\r
783 \*---------------------------------------------------------------------------*/\r
784 \r
785 int APIENTRY\r
786 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
787         LPSTR lpCmdLine, int nCmdShow)\r
788 {\r
789   MSG msg;\r
790   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
791 //  INITCOMMONCONTROLSEX ex;\r
792 \r
793   debugFP = stderr;\r
794 \r
795   LoadLibrary("RICHED32.DLL");\r
796   consoleCF.cbSize = sizeof(CHARFORMAT);\r
797 \r
798   if (!InitApplication(hInstance)) {\r
799     return (FALSE);\r
800   }\r
801   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
802     return (FALSE);\r
803   }\r
804 \r
805   JAWS_INIT\r
806   TranslateMenus(1);\r
807 \r
808 //  InitCommonControlsEx(&ex);\r
809   InitCommonControls();\r
810 \r
811   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
812   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
813   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
814 \r
815   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
816 \r
817   while (GetMessage(&msg, /* message structure */\r
818                     NULL, /* handle of window receiving the message */\r
819                     0,    /* lowest message to examine */\r
820                     0))   /* highest message to examine */\r
821     {\r
822 \r
823       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
824         // [HGM] navigate: switch between all windows with tab\r
825         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
826         int i, currentElement = 0;\r
827 \r
828         // first determine what element of the chain we come from (if any)\r
829         if(appData.icsActive) {\r
830             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
831             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
832         }\r
833         if(engineOutputDialog && EngineOutputIsUp()) {\r
834             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
835             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
836         }\r
837         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
838             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
839         }\r
840         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
841         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
842         if(msg.hwnd == e1)                 currentElement = 2; else\r
843         if(msg.hwnd == e2)                 currentElement = 3; else\r
844         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
845         if(msg.hwnd == mh)                currentElement = 4; else\r
846         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
847         if(msg.hwnd == hText)  currentElement = 5; else\r
848         if(msg.hwnd == hInput) currentElement = 6; else\r
849         for (i = 0; i < N_BUTTONS; i++) {\r
850             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
851         }\r
852 \r
853         // determine where to go to\r
854         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
855           do {\r
856             currentElement = (currentElement + direction) % 7;\r
857             switch(currentElement) {\r
858                 case 0:\r
859                   h = hwndMain; break; // passing this case always makes the loop exit\r
860                 case 1:\r
861                   h = buttonDesc[0].hwnd; break; // could be NULL\r
862                 case 2:\r
863                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
864                   h = e1; break;\r
865                 case 3:\r
866                   if(!EngineOutputIsUp()) continue;\r
867                   h = e2; break;\r
868                 case 4:\r
869                   if(!MoveHistoryIsUp()) continue;\r
870                   h = mh; break;\r
871 //              case 6: // input to eval graph does not seem to get here!\r
872 //                if(!EvalGraphIsUp()) continue;\r
873 //                h = evalGraphDialog; break;\r
874                 case 5:\r
875                   if(!appData.icsActive) continue;\r
876                   SAY("display");\r
877                   h = hText; break;\r
878                 case 6:\r
879                   if(!appData.icsActive) continue;\r
880                   SAY("input");\r
881                   h = hInput; break;\r
882             }\r
883           } while(h == 0);\r
884 \r
885           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
886           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
887           SetFocus(h);\r
888 \r
889           continue; // this message now has been processed\r
890         }\r
891       }\r
892 \r
893       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
894           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
895           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
896           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
897           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
898           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
899           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
900           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
901           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
902           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
903         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
904         for(i=0; i<MAX_CHAT; i++) \r
905             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
906                 done = 1; break;\r
907         }\r
908         if(done) continue; // [HGM] chat: end patch\r
909         TranslateMessage(&msg); /* Translates virtual key codes */\r
910         DispatchMessage(&msg);  /* Dispatches message to window */\r
911       }\r
912     }\r
913 \r
914 \r
915   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
916 }\r
917 \r
918 /*---------------------------------------------------------------------------*\\r
919  *\r
920  * Initialization functions\r
921  *\r
922 \*---------------------------------------------------------------------------*/\r
923 \r
924 void\r
925 SetUserLogo()\r
926 {   // update user logo if necessary\r
927     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
928 \r
929     if(appData.autoLogo) {\r
930           curName = UserName();\r
931           if(strcmp(curName, oldUserName)) {\r
932                 GetCurrentDirectory(MSG_SIZ, dir);\r
933                 SetCurrentDirectory(installDir);\r
934                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
935                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
936                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
937                 if(userLogo == NULL)\r
938                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
939                 SetCurrentDirectory(dir); /* return to prev directory */\r
940           }\r
941     }\r
942 }\r
943 \r
944 BOOL\r
945 InitApplication(HINSTANCE hInstance)\r
946 {\r
947   WNDCLASS wc;\r
948 \r
949   /* Fill in window class structure with parameters that describe the */\r
950   /* main window. */\r
951 \r
952   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
953   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
954   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
955   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
956   wc.hInstance     = hInstance;         /* Owner of this class */\r
957   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
958   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
959   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
960   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
961   wc.lpszClassName = szAppName;                 /* Name to register as */\r
962 \r
963   /* Register the window class and return success/failure code. */\r
964   if (!RegisterClass(&wc)) return FALSE;\r
965 \r
966   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
967   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
968   wc.cbClsExtra    = 0;\r
969   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
970   wc.hInstance     = hInstance;\r
971   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
972   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
973   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
974   wc.lpszMenuName  = NULL;\r
975   wc.lpszClassName = szConsoleName;\r
976 \r
977   if (!RegisterClass(&wc)) return FALSE;\r
978   return TRUE;\r
979 }\r
980 \r
981 \r
982 /* Set by InitInstance, used by EnsureOnScreen */\r
983 int screenHeight, screenWidth;\r
984 \r
985 void\r
986 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
987 {\r
988 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
989   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
990   if (*x > screenWidth - 32) *x = 0;\r
991   if (*y > screenHeight - 32) *y = 0;\r
992   if (*x < minX) *x = minX;\r
993   if (*y < minY) *y = minY;\r
994 }\r
995 \r
996 VOID\r
997 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
998 {\r
999   char buf[MSG_SIZ], dir[MSG_SIZ];\r
1000   GetCurrentDirectory(MSG_SIZ, dir);\r
1001   SetCurrentDirectory(installDir);\r
1002   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
1003       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1004 \r
1005       if (cps->programLogo == NULL && appData.debugMode) {\r
1006           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1007       }\r
1008   } else if(appData.autoLogo) {\r
1009       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1010         char *opponent = "";\r
1011         if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;\r
1012         if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;\r
1013         sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);\r
1014         if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {\r
1015             sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1016             cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1017         }\r
1018       } else\r
1019       if(appData.directory[n] && appData.directory[n][0]) {\r
1020         SetCurrentDirectory(appData.directory[n]);\r
1021         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1022       }\r
1023   }\r
1024   SetCurrentDirectory(dir); /* return to prev directory */\r
1025 }\r
1026 \r
1027 VOID\r
1028 InitTextures()\r
1029 {\r
1030   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1031   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1032   \r
1033   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1034       if(liteBackTexture) DeleteObject(liteBackTexture);\r
1035       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1036       liteBackTextureMode = appData.liteBackTextureMode;\r
1037 \r
1038       if (liteBackTexture == NULL && appData.debugMode) {\r
1039           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1040       }\r
1041   }\r
1042   \r
1043   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1044       if(darkBackTexture) DeleteObject(darkBackTexture);\r
1045       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1046       darkBackTextureMode = appData.darkBackTextureMode;\r
1047 \r
1048       if (darkBackTexture == NULL && appData.debugMode) {\r
1049           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1050       }\r
1051   }\r
1052 }\r
1053 \r
1054 BOOL\r
1055 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1056 {\r
1057   HWND hwnd; /* Main window handle. */\r
1058   int ibs;\r
1059   WINDOWPLACEMENT wp;\r
1060   char *filepart;\r
1061 \r
1062   hInst = hInstance;    /* Store instance handle in our global variable */\r
1063   programName = szAppName;\r
1064 \r
1065   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1066     *filepart = NULLCHAR;\r
1067     SetCurrentDirectory(installDir);\r
1068   } else {\r
1069     GetCurrentDirectory(MSG_SIZ, installDir);\r
1070   }\r
1071   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1072   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
1073   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1074   /* xboard, and older WinBoards, controlled the move sound with the\r
1075      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1076      always turn the option on (so that the backend will call us),\r
1077      then let the user turn the sound off by setting it to silence if\r
1078      desired.  To accommodate old winboard.ini files saved by old\r
1079      versions of WinBoard, we also turn off the sound if the option\r
1080      was initially set to false. [HGM] taken out of InitAppData */\r
1081   if (!appData.ringBellAfterMoves) {\r
1082     sounds[(int)SoundMove].name = strdup("");\r
1083     appData.ringBellAfterMoves = TRUE;\r
1084   }\r
1085   if (appData.debugMode) {\r
1086     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1087     setbuf(debugFP, NULL);\r
1088   }\r
1089 \r
1090   LoadLanguageFile(appData.language);\r
1091 \r
1092   InitBackEnd1();\r
1093 \r
1094 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1095 //  InitEngineUCI( installDir, &second );\r
1096 \r
1097   /* Create a main window for this application instance. */\r
1098   hwnd = CreateWindow(szAppName, szTitle,\r
1099                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1100                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1101                       NULL, NULL, hInstance, NULL);\r
1102   hwndMain = hwnd;\r
1103 \r
1104   /* If window could not be created, return "failure" */\r
1105   if (!hwnd) {\r
1106     return (FALSE);\r
1107   }\r
1108 \r
1109   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1110   LoadLogo(&first, 0, FALSE);\r
1111   LoadLogo(&second, 1, appData.icsActive);\r
1112 \r
1113   SetUserLogo();\r
1114 \r
1115   iconWhite = LoadIcon(hInstance, "icon_white");\r
1116   iconBlack = LoadIcon(hInstance, "icon_black");\r
1117   iconCurrent = iconWhite;\r
1118   InitDrawingColors();\r
1119   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1120   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1121   InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args\r
1122   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1123     /* Compute window size for each board size, and use the largest\r
1124        size that fits on this screen as the default. */\r
1125     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1126     if (boardSize == (BoardSize)-1 &&\r
1127         winH <= screenHeight\r
1128            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1129         && winW <= screenWidth) {\r
1130       boardSize = (BoardSize)ibs;\r
1131     }\r
1132   }\r
1133 \r
1134   InitDrawingSizes(boardSize, 0);\r
1135   RecentEngineMenu(appData.recentEngineList);\r
1136   InitMenuChecks();\r
1137   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1138 \r
1139   /* [AS] Load textures if specified */\r
1140   InitTextures();\r
1141 \r
1142   mysrandom( (unsigned) time(NULL) );\r
1143 \r
1144   /* [AS] Restore layout */\r
1145   if( wpMoveHistory.visible ) {\r
1146       MoveHistoryPopUp();\r
1147   }\r
1148 \r
1149   if( wpEvalGraph.visible ) {\r
1150       EvalGraphPopUp();\r
1151   }\r
1152 \r
1153   if( wpEngineOutput.visible ) {\r
1154       EngineOutputPopUp();\r
1155   }\r
1156 \r
1157   /* Make the window visible; update its client area; and return "success" */\r
1158   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1159   wp.length = sizeof(WINDOWPLACEMENT);\r
1160   wp.flags = 0;\r
1161   wp.showCmd = nCmdShow;\r
1162   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1163   wp.rcNormalPosition.left = wpMain.x;\r
1164   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1165   wp.rcNormalPosition.top = wpMain.y;\r
1166   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1167   SetWindowPlacement(hwndMain, &wp);\r
1168 \r
1169   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1170 \r
1171   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1172                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1173 \r
1174   if (hwndConsole) {\r
1175 #if AOT_CONSOLE\r
1176     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1177                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1178 #endif\r
1179     ShowWindow(hwndConsole, nCmdShow);\r
1180     SetActiveWindow(hwndConsole);\r
1181   }\r
1182   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1183   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1184 \r
1185   return TRUE;\r
1186 \r
1187 }\r
1188 \r
1189 VOID\r
1190 InitMenuChecks()\r
1191 {\r
1192   HMENU hmenu = GetMenu(hwndMain);\r
1193 \r
1194   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1195                         MF_BYCOMMAND|((appData.icsActive &&\r
1196                                        *appData.icsCommPort != NULLCHAR) ?\r
1197                                       MF_ENABLED : MF_GRAYED));\r
1198   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1199                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1200                                      MF_CHECKED : MF_UNCHECKED));\r
1201 }\r
1202 \r
1203 //---------------------------------------------------------------------------------------------------------\r
1204 \r
1205 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1206 #define XBOARD FALSE\r
1207 \r
1208 #define OPTCHAR "/"\r
1209 #define SEPCHAR "="\r
1210 #define TOPLEVEL 0\r
1211 \r
1212 #include "args.h"\r
1213 \r
1214 // front-end part of option handling\r
1215 \r
1216 VOID\r
1217 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1218 {\r
1219   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1220   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1221   DeleteDC(hdc);\r
1222   lf->lfWidth = 0;\r
1223   lf->lfEscapement = 0;\r
1224   lf->lfOrientation = 0;\r
1225   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1226   lf->lfItalic = mfp->italic;\r
1227   lf->lfUnderline = mfp->underline;\r
1228   lf->lfStrikeOut = mfp->strikeout;\r
1229   lf->lfCharSet = mfp->charset;\r
1230   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1231   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1232   lf->lfQuality = DEFAULT_QUALITY;\r
1233   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1234     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1235 }\r
1236 \r
1237 void\r
1238 CreateFontInMF(MyFont *mf)\r
1239\r
1240   LFfromMFP(&mf->lf, &mf->mfp);\r
1241   if (mf->hf) DeleteObject(mf->hf);\r
1242   mf->hf = CreateFontIndirect(&mf->lf);\r
1243 }\r
1244 \r
1245 // [HGM] This platform-dependent table provides the location for storing the color info\r
1246 void *\r
1247 colorVariable[] = {\r
1248   &whitePieceColor, \r
1249   &blackPieceColor, \r
1250   &lightSquareColor,\r
1251   &darkSquareColor, \r
1252   &highlightSquareColor,\r
1253   &premoveHighlightColor,\r
1254   NULL,\r
1255   &consoleBackgroundColor,\r
1256   &appData.fontForeColorWhite,\r
1257   &appData.fontBackColorWhite,\r
1258   &appData.fontForeColorBlack,\r
1259   &appData.fontBackColorBlack,\r
1260   &appData.evalHistColorWhite,\r
1261   &appData.evalHistColorBlack,\r
1262   &appData.highlightArrowColor,\r
1263 };\r
1264 \r
1265 /* Command line font name parser.  NULL name means do nothing.\r
1266    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1267    For backward compatibility, syntax without the colon is also\r
1268    accepted, but font names with digits in them won't work in that case.\r
1269 */\r
1270 VOID\r
1271 ParseFontName(char *name, MyFontParams *mfp)\r
1272 {\r
1273   char *p, *q;\r
1274   if (name == NULL) return;\r
1275   p = name;\r
1276   q = strchr(p, ':');\r
1277   if (q) {\r
1278     if (q - p >= sizeof(mfp->faceName))\r
1279       ExitArgError(_("Font name too long:"), name, TRUE);\r
1280     memcpy(mfp->faceName, p, q - p);\r
1281     mfp->faceName[q - p] = NULLCHAR;\r
1282     p = q + 1;\r
1283   } else {\r
1284     q = mfp->faceName;\r
1285 \r
1286     while (*p && !isdigit(*p)) {\r
1287       *q++ = *p++;\r
1288       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1289         ExitArgError(_("Font name too long:"), name, TRUE);\r
1290     }\r
1291     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1292     *q = NULLCHAR;\r
1293   }\r
1294   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1295   mfp->pointSize = (float) atof(p);\r
1296   mfp->bold = (strchr(p, 'b') != NULL);\r
1297   mfp->italic = (strchr(p, 'i') != NULL);\r
1298   mfp->underline = (strchr(p, 'u') != NULL);\r
1299   mfp->strikeout = (strchr(p, 's') != NULL);\r
1300   mfp->charset = DEFAULT_CHARSET;\r
1301   q = strchr(p, 'c');\r
1302   if (q)\r
1303     mfp->charset = (BYTE) atoi(q+1);\r
1304 }\r
1305 \r
1306 void\r
1307 ParseFont(char *name, int number)\r
1308 { // wrapper to shield back-end from 'font'\r
1309   ParseFontName(name, &font[boardSize][number]->mfp);\r
1310 }\r
1311 \r
1312 void\r
1313 SetFontDefaults()\r
1314 { // in WB  we have a 2D array of fonts; this initializes their description\r
1315   int i, j;\r
1316   /* Point font array elements to structures and\r
1317      parse default font names */\r
1318   for (i=0; i<NUM_FONTS; i++) {\r
1319     for (j=0; j<NUM_SIZES; j++) {\r
1320       font[j][i] = &fontRec[j][i];\r
1321       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1322     }\r
1323   }\r
1324 }\r
1325 \r
1326 void\r
1327 CreateFonts()\r
1328 { // here we create the actual fonts from the selected descriptions\r
1329   int i, j;\r
1330   for (i=0; i<NUM_FONTS; i++) {\r
1331     for (j=0; j<NUM_SIZES; j++) {\r
1332       CreateFontInMF(font[j][i]);\r
1333     }\r
1334   }\r
1335 }\r
1336 /* Color name parser.\r
1337    X version accepts X color names, but this one\r
1338    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1339 COLORREF\r
1340 ParseColorName(char *name)\r
1341 {\r
1342   int red, green, blue, count;\r
1343   char buf[MSG_SIZ];\r
1344 \r
1345   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1346   if (count != 3) {\r
1347     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1348       &red, &green, &blue);\r
1349   }\r
1350   if (count != 3) {\r
1351     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1352     DisplayError(buf, 0);\r
1353     return RGB(0, 0, 0);\r
1354   }\r
1355   return PALETTERGB(red, green, blue);\r
1356 }\r
1357 \r
1358 void\r
1359 ParseColor(int n, char *name)\r
1360 { // for WinBoard the color is an int, which needs to be derived from the string\r
1361   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1362 }\r
1363 \r
1364 void\r
1365 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1366 {\r
1367   char *e = argValue;\r
1368   int eff = 0;\r
1369 \r
1370   while (*e) {\r
1371     if (*e == 'b')      eff |= CFE_BOLD;\r
1372     else if (*e == 'i') eff |= CFE_ITALIC;\r
1373     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1374     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1375     else if (*e == '#' || isdigit(*e)) break;\r
1376     e++;\r
1377   }\r
1378   *effects = eff;\r
1379   *color   = ParseColorName(e);\r
1380 }\r
1381 \r
1382 void\r
1383 ParseTextAttribs(ColorClass cc, char *s)\r
1384 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1385     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1386     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1387 }\r
1388 \r
1389 void\r
1390 ParseBoardSize(void *addr, char *name)\r
1391 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1392   BoardSize bs = SizeTiny;\r
1393   while (sizeInfo[bs].name != NULL) {\r
1394     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1395         *(BoardSize *)addr = bs;\r
1396         return;\r
1397     }\r
1398     bs++;\r
1399   }\r
1400   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1401 }\r
1402 \r
1403 void\r
1404 LoadAllSounds()\r
1405 { // [HGM] import name from appData first\r
1406   ColorClass cc;\r
1407   SoundClass sc;\r
1408   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1409     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1410     textAttribs[cc].sound.data = NULL;\r
1411     MyLoadSound(&textAttribs[cc].sound);\r
1412   }\r
1413   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1414     textAttribs[cc].sound.name = strdup("");\r
1415     textAttribs[cc].sound.data = NULL;\r
1416   }\r
1417   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1418     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1419     sounds[sc].data = NULL;\r
1420     MyLoadSound(&sounds[sc]);\r
1421   }\r
1422 }\r
1423 \r
1424 void\r
1425 SetCommPortDefaults()\r
1426 {\r
1427    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1428   dcb.DCBlength = sizeof(DCB);\r
1429   dcb.BaudRate = 9600;\r
1430   dcb.fBinary = TRUE;\r
1431   dcb.fParity = FALSE;\r
1432   dcb.fOutxCtsFlow = FALSE;\r
1433   dcb.fOutxDsrFlow = FALSE;\r
1434   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1435   dcb.fDsrSensitivity = FALSE;\r
1436   dcb.fTXContinueOnXoff = TRUE;\r
1437   dcb.fOutX = FALSE;\r
1438   dcb.fInX = FALSE;\r
1439   dcb.fNull = FALSE;\r
1440   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1441   dcb.fAbortOnError = FALSE;\r
1442   dcb.ByteSize = 7;\r
1443   dcb.Parity = SPACEPARITY;\r
1444   dcb.StopBits = ONESTOPBIT;\r
1445 }\r
1446 \r
1447 // [HGM] args: these three cases taken out to stay in front-end\r
1448 void\r
1449 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1450 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1451         // while the curent board size determines the element. This system should be ported to XBoard.\r
1452         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1453         int bs;\r
1454         for (bs=0; bs<NUM_SIZES; bs++) {\r
1455           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1456           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1457           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1458             ad->argName, mfp->faceName, mfp->pointSize,\r
1459             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1460             mfp->bold ? "b" : "",\r
1461             mfp->italic ? "i" : "",\r
1462             mfp->underline ? "u" : "",\r
1463             mfp->strikeout ? "s" : "",\r
1464             (int)mfp->charset);\r
1465         }\r
1466       }\r
1467 \r
1468 void\r
1469 ExportSounds()\r
1470 { // [HGM] copy the names from the internal WB variables to appData\r
1471   ColorClass cc;\r
1472   SoundClass sc;\r
1473   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1474     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1475   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1476     (&appData.soundMove)[sc] = sounds[sc].name;\r
1477 }\r
1478 \r
1479 void\r
1480 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1481 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1482         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1483         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1484           (ta->effects & CFE_BOLD) ? "b" : "",\r
1485           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1486           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1487           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1488           (ta->effects) ? " " : "",\r
1489           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1490       }\r
1491 \r
1492 void\r
1493 SaveColor(FILE *f, ArgDescriptor *ad)\r
1494 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1495         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1496         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1497           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1498 }\r
1499 \r
1500 void\r
1501 SaveBoardSize(FILE *f, char *name, void *addr)\r
1502 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1503   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1504 }\r
1505 \r
1506 void\r
1507 ParseCommPortSettings(char *s)\r
1508 { // wrapper to keep dcb from back-end\r
1509   ParseCommSettings(s, &dcb);\r
1510 }\r
1511 \r
1512 void\r
1513 GetWindowCoords()\r
1514 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1515   GetActualPlacement(hwndMain, &wpMain);\r
1516   GetActualPlacement(hwndConsole, &wpConsole);\r
1517   GetActualPlacement(commentDialog, &wpComment);\r
1518   GetActualPlacement(editTagsDialog, &wpTags);\r
1519   GetActualPlacement(gameListDialog, &wpGameList);\r
1520   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1521   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1522   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1523 }\r
1524 \r
1525 void\r
1526 PrintCommPortSettings(FILE *f, char *name)\r
1527 { // wrapper to shield back-end from DCB\r
1528       PrintCommSettings(f, name, &dcb);\r
1529 }\r
1530 \r
1531 int\r
1532 MySearchPath(char *installDir, char *name, char *fullname)\r
1533 {\r
1534   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1535   if(name[0]== '%') {\r
1536     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1537     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1538       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1539       *strchr(buf, '%') = 0;\r
1540       strcat(fullname, getenv(buf));\r
1541       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1542     }\r
1543     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1544     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1545     return (int) strlen(fullname);\r
1546   }\r
1547   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1548 }\r
1549 \r
1550 int\r
1551 MyGetFullPathName(char *name, char *fullname)\r
1552 {\r
1553   char *dummy;\r
1554   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1555 }\r
1556 \r
1557 int\r
1558 MainWindowUp()\r
1559 { // [HGM] args: allows testing if main window is realized from back-end\r
1560   return hwndMain != NULL;\r
1561 }\r
1562 \r
1563 void\r
1564 PopUpStartupDialog()\r
1565 {\r
1566     FARPROC lpProc;\r
1567     \r
1568     LoadLanguageFile(appData.language);\r
1569     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1570     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1571     FreeProcInstance(lpProc);\r
1572 }\r
1573 \r
1574 /*---------------------------------------------------------------------------*\\r
1575  *\r
1576  * GDI board drawing routines\r
1577  *\r
1578 \*---------------------------------------------------------------------------*/\r
1579 \r
1580 /* [AS] Draw square using background texture */\r
1581 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1582 {\r
1583     XFORM   x;\r
1584 \r
1585     if( mode == 0 ) {\r
1586         return; /* Should never happen! */\r
1587     }\r
1588 \r
1589     SetGraphicsMode( dst, GM_ADVANCED );\r
1590 \r
1591     switch( mode ) {\r
1592     case 1:\r
1593         /* Identity */\r
1594         break;\r
1595     case 2:\r
1596         /* X reflection */\r
1597         x.eM11 = -1.0;\r
1598         x.eM12 = 0;\r
1599         x.eM21 = 0;\r
1600         x.eM22 = 1.0;\r
1601         x.eDx = (FLOAT) dw + dx - 1;\r
1602         x.eDy = 0;\r
1603         dx = 0;\r
1604         SetWorldTransform( dst, &x );\r
1605         break;\r
1606     case 3:\r
1607         /* Y reflection */\r
1608         x.eM11 = 1.0;\r
1609         x.eM12 = 0;\r
1610         x.eM21 = 0;\r
1611         x.eM22 = -1.0;\r
1612         x.eDx = 0;\r
1613         x.eDy = (FLOAT) dh + dy - 1;\r
1614         dy = 0;\r
1615         SetWorldTransform( dst, &x );\r
1616         break;\r
1617     case 4:\r
1618         /* X/Y flip */\r
1619         x.eM11 = 0;\r
1620         x.eM12 = 1.0;\r
1621         x.eM21 = 1.0;\r
1622         x.eM22 = 0;\r
1623         x.eDx = (FLOAT) dx;\r
1624         x.eDy = (FLOAT) dy;\r
1625         dx = 0;\r
1626         dy = 0;\r
1627         SetWorldTransform( dst, &x );\r
1628         break;\r
1629     }\r
1630 \r
1631     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1632 \r
1633     x.eM11 = 1.0;\r
1634     x.eM12 = 0;\r
1635     x.eM21 = 0;\r
1636     x.eM22 = 1.0;\r
1637     x.eDx = 0;\r
1638     x.eDy = 0;\r
1639     SetWorldTransform( dst, &x );\r
1640 \r
1641     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1642 }\r
1643 \r
1644 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1645 enum {\r
1646     PM_WP = (int) WhitePawn, \r
1647     PM_WN = (int) WhiteKnight, \r
1648     PM_WB = (int) WhiteBishop, \r
1649     PM_WR = (int) WhiteRook, \r
1650     PM_WQ = (int) WhiteQueen, \r
1651     PM_WF = (int) WhiteFerz, \r
1652     PM_WW = (int) WhiteWazir, \r
1653     PM_WE = (int) WhiteAlfil, \r
1654     PM_WM = (int) WhiteMan, \r
1655     PM_WO = (int) WhiteCannon, \r
1656     PM_WU = (int) WhiteUnicorn, \r
1657     PM_WH = (int) WhiteNightrider, \r
1658     PM_WA = (int) WhiteAngel, \r
1659     PM_WC = (int) WhiteMarshall, \r
1660     PM_WAB = (int) WhiteCardinal, \r
1661     PM_WD = (int) WhiteDragon, \r
1662     PM_WL = (int) WhiteLance, \r
1663     PM_WS = (int) WhiteCobra, \r
1664     PM_WV = (int) WhiteFalcon, \r
1665     PM_WSG = (int) WhiteSilver, \r
1666     PM_WG = (int) WhiteGrasshopper, \r
1667     PM_WK = (int) WhiteKing,\r
1668     PM_BP = (int) BlackPawn, \r
1669     PM_BN = (int) BlackKnight, \r
1670     PM_BB = (int) BlackBishop, \r
1671     PM_BR = (int) BlackRook, \r
1672     PM_BQ = (int) BlackQueen, \r
1673     PM_BF = (int) BlackFerz, \r
1674     PM_BW = (int) BlackWazir, \r
1675     PM_BE = (int) BlackAlfil, \r
1676     PM_BM = (int) BlackMan,\r
1677     PM_BO = (int) BlackCannon, \r
1678     PM_BU = (int) BlackUnicorn, \r
1679     PM_BH = (int) BlackNightrider, \r
1680     PM_BA = (int) BlackAngel, \r
1681     PM_BC = (int) BlackMarshall, \r
1682     PM_BG = (int) BlackGrasshopper, \r
1683     PM_BAB = (int) BlackCardinal,\r
1684     PM_BD = (int) BlackDragon,\r
1685     PM_BL = (int) BlackLance,\r
1686     PM_BS = (int) BlackCobra,\r
1687     PM_BV = (int) BlackFalcon,\r
1688     PM_BSG = (int) BlackSilver,\r
1689     PM_BK = (int) BlackKing\r
1690 };\r
1691 \r
1692 static HFONT hPieceFont = NULL;\r
1693 static HBITMAP hPieceMask[(int) EmptySquare];\r
1694 static HBITMAP hPieceFace[(int) EmptySquare];\r
1695 static int fontBitmapSquareSize = 0;\r
1696 static char pieceToFontChar[(int) EmptySquare] =\r
1697                               { 'p', 'n', 'b', 'r', 'q', \r
1698                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1699                       'k', 'o', 'm', 'v', 't', 'w', \r
1700                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1701                                                               'l' };\r
1702 \r
1703 extern BOOL SetCharTable( char *table, const char * map );\r
1704 /* [HGM] moved to backend.c */\r
1705 \r
1706 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1707 {\r
1708     HBRUSH hbrush;\r
1709     BYTE r1 = GetRValue( color );\r
1710     BYTE g1 = GetGValue( color );\r
1711     BYTE b1 = GetBValue( color );\r
1712     BYTE r2 = r1 / 2;\r
1713     BYTE g2 = g1 / 2;\r
1714     BYTE b2 = b1 / 2;\r
1715     RECT rc;\r
1716 \r
1717     /* Create a uniform background first */\r
1718     hbrush = CreateSolidBrush( color );\r
1719     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1720     FillRect( hdc, &rc, hbrush );\r
1721     DeleteObject( hbrush );\r
1722     \r
1723     if( mode == 1 ) {\r
1724         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1725         int steps = squareSize / 2;\r
1726         int i;\r
1727 \r
1728         for( i=0; i<steps; i++ ) {\r
1729             BYTE r = r1 - (r1-r2) * i / steps;\r
1730             BYTE g = g1 - (g1-g2) * i / steps;\r
1731             BYTE b = b1 - (b1-b2) * i / steps;\r
1732 \r
1733             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1734             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1735             FillRect( hdc, &rc, hbrush );\r
1736             DeleteObject(hbrush);\r
1737         }\r
1738     }\r
1739     else if( mode == 2 ) {\r
1740         /* Diagonal gradient, good more or less for every piece */\r
1741         POINT triangle[3];\r
1742         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1743         HBRUSH hbrush_old;\r
1744         int steps = squareSize;\r
1745         int i;\r
1746 \r
1747         triangle[0].x = squareSize - steps;\r
1748         triangle[0].y = squareSize;\r
1749         triangle[1].x = squareSize;\r
1750         triangle[1].y = squareSize;\r
1751         triangle[2].x = squareSize;\r
1752         triangle[2].y = squareSize - steps;\r
1753 \r
1754         for( i=0; i<steps; i++ ) {\r
1755             BYTE r = r1 - (r1-r2) * i / steps;\r
1756             BYTE g = g1 - (g1-g2) * i / steps;\r
1757             BYTE b = b1 - (b1-b2) * i / steps;\r
1758 \r
1759             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1760             hbrush_old = SelectObject( hdc, hbrush );\r
1761             Polygon( hdc, triangle, 3 );\r
1762             SelectObject( hdc, hbrush_old );\r
1763             DeleteObject(hbrush);\r
1764             triangle[0].x++;\r
1765             triangle[2].y++;\r
1766         }\r
1767 \r
1768         SelectObject( hdc, hpen );\r
1769     }\r
1770 }\r
1771 \r
1772 /*\r
1773     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1774     seems to work ok. The main problem here is to find the "inside" of a chess\r
1775     piece: follow the steps as explained below.\r
1776 */\r
1777 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1778 {\r
1779     HBITMAP hbm;\r
1780     HBITMAP hbm_old;\r
1781     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1782     RECT rc;\r
1783     SIZE sz;\r
1784     POINT pt;\r
1785     int backColor = whitePieceColor; \r
1786     int foreColor = blackPieceColor;\r
1787     \r
1788     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1789         backColor = appData.fontBackColorWhite;\r
1790         foreColor = appData.fontForeColorWhite;\r
1791     }\r
1792     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1793         backColor = appData.fontBackColorBlack;\r
1794         foreColor = appData.fontForeColorBlack;\r
1795     }\r
1796 \r
1797     /* Mask */\r
1798     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1799 \r
1800     hbm_old = SelectObject( hdc, hbm );\r
1801 \r
1802     rc.left = 0;\r
1803     rc.top = 0;\r
1804     rc.right = squareSize;\r
1805     rc.bottom = squareSize;\r
1806 \r
1807     /* Step 1: background is now black */\r
1808     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1809 \r
1810     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1811 \r
1812     pt.x = (squareSize - sz.cx) / 2;\r
1813     pt.y = (squareSize - sz.cy) / 2;\r
1814 \r
1815     SetBkMode( hdc, TRANSPARENT );\r
1816     SetTextColor( hdc, chroma );\r
1817     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1818     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1819 \r
1820     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1821     /* Step 3: the area outside the piece is filled with white */\r
1822 //    FloodFill( hdc, 0, 0, chroma );\r
1823     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1824     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1825     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1826     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1827     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1828     /* \r
1829         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1830         but if the start point is not inside the piece we're lost!\r
1831         There should be a better way to do this... if we could create a region or path\r
1832         from the fill operation we would be fine for example.\r
1833     */\r
1834 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1835     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1836 \r
1837     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1838         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1839         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1840 \r
1841         SelectObject( dc2, bm2 );\r
1842         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1843         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1844         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1845         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1846         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1847 \r
1848         DeleteDC( dc2 );\r
1849         DeleteObject( bm2 );\r
1850     }\r
1851 \r
1852     SetTextColor( hdc, 0 );\r
1853     /* \r
1854         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1855         draw the piece again in black for safety.\r
1856     */\r
1857     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1858 \r
1859     SelectObject( hdc, hbm_old );\r
1860 \r
1861     if( hPieceMask[index] != NULL ) {\r
1862         DeleteObject( hPieceMask[index] );\r
1863     }\r
1864 \r
1865     hPieceMask[index] = hbm;\r
1866 \r
1867     /* Face */\r
1868     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1869 \r
1870     SelectObject( hdc, hbm );\r
1871 \r
1872     {\r
1873         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1874         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1875         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1876 \r
1877         SelectObject( dc1, hPieceMask[index] );\r
1878         SelectObject( dc2, bm2 );\r
1879         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1880         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1881         \r
1882         /* \r
1883             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1884             the piece background and deletes (makes transparent) the rest.\r
1885             Thanks to that mask, we are free to paint the background with the greates\r
1886             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1887             We use this, to make gradients and give the pieces a "roundish" look.\r
1888         */\r
1889         SetPieceBackground( hdc, backColor, 2 );\r
1890         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1891 \r
1892         DeleteDC( dc2 );\r
1893         DeleteDC( dc1 );\r
1894         DeleteObject( bm2 );\r
1895     }\r
1896 \r
1897     SetTextColor( hdc, foreColor );\r
1898     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1899 \r
1900     SelectObject( hdc, hbm_old );\r
1901 \r
1902     if( hPieceFace[index] != NULL ) {\r
1903         DeleteObject( hPieceFace[index] );\r
1904     }\r
1905 \r
1906     hPieceFace[index] = hbm;\r
1907 }\r
1908 \r
1909 static int TranslatePieceToFontPiece( int piece )\r
1910 {\r
1911     switch( piece ) {\r
1912     case BlackPawn:\r
1913         return PM_BP;\r
1914     case BlackKnight:\r
1915         return PM_BN;\r
1916     case BlackBishop:\r
1917         return PM_BB;\r
1918     case BlackRook:\r
1919         return PM_BR;\r
1920     case BlackQueen:\r
1921         return PM_BQ;\r
1922     case BlackKing:\r
1923         return PM_BK;\r
1924     case WhitePawn:\r
1925         return PM_WP;\r
1926     case WhiteKnight:\r
1927         return PM_WN;\r
1928     case WhiteBishop:\r
1929         return PM_WB;\r
1930     case WhiteRook:\r
1931         return PM_WR;\r
1932     case WhiteQueen:\r
1933         return PM_WQ;\r
1934     case WhiteKing:\r
1935         return PM_WK;\r
1936 \r
1937     case BlackAngel:\r
1938         return PM_BA;\r
1939     case BlackMarshall:\r
1940         return PM_BC;\r
1941     case BlackFerz:\r
1942         return PM_BF;\r
1943     case BlackNightrider:\r
1944         return PM_BH;\r
1945     case BlackAlfil:\r
1946         return PM_BE;\r
1947     case BlackWazir:\r
1948         return PM_BW;\r
1949     case BlackUnicorn:\r
1950         return PM_BU;\r
1951     case BlackCannon:\r
1952         return PM_BO;\r
1953     case BlackGrasshopper:\r
1954         return PM_BG;\r
1955     case BlackMan:\r
1956         return PM_BM;\r
1957     case BlackSilver:\r
1958         return PM_BSG;\r
1959     case BlackLance:\r
1960         return PM_BL;\r
1961     case BlackFalcon:\r
1962         return PM_BV;\r
1963     case BlackCobra:\r
1964         return PM_BS;\r
1965     case BlackCardinal:\r
1966         return PM_BAB;\r
1967     case BlackDragon:\r
1968         return PM_BD;\r
1969 \r
1970     case WhiteAngel:\r
1971         return PM_WA;\r
1972     case WhiteMarshall:\r
1973         return PM_WC;\r
1974     case WhiteFerz:\r
1975         return PM_WF;\r
1976     case WhiteNightrider:\r
1977         return PM_WH;\r
1978     case WhiteAlfil:\r
1979         return PM_WE;\r
1980     case WhiteWazir:\r
1981         return PM_WW;\r
1982     case WhiteUnicorn:\r
1983         return PM_WU;\r
1984     case WhiteCannon:\r
1985         return PM_WO;\r
1986     case WhiteGrasshopper:\r
1987         return PM_WG;\r
1988     case WhiteMan:\r
1989         return PM_WM;\r
1990     case WhiteSilver:\r
1991         return PM_WSG;\r
1992     case WhiteLance:\r
1993         return PM_WL;\r
1994     case WhiteFalcon:\r
1995         return PM_WV;\r
1996     case WhiteCobra:\r
1997         return PM_WS;\r
1998     case WhiteCardinal:\r
1999         return PM_WAB;\r
2000     case WhiteDragon:\r
2001         return PM_WD;\r
2002     }\r
2003 \r
2004     return 0;\r
2005 }\r
2006 \r
2007 void CreatePiecesFromFont()\r
2008 {\r
2009     LOGFONT lf;\r
2010     HDC hdc_window = NULL;\r
2011     HDC hdc = NULL;\r
2012     HFONT hfont_old;\r
2013     int fontHeight;\r
2014     int i;\r
2015 \r
2016     if( fontBitmapSquareSize < 0 ) {\r
2017         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2018         return;\r
2019     }\r
2020 \r
2021     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2022             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2023         fontBitmapSquareSize = -1;\r
2024         return;\r
2025     }\r
2026 \r
2027     if( fontBitmapSquareSize != squareSize ) {\r
2028         hdc_window = GetDC( hwndMain );\r
2029         hdc = CreateCompatibleDC( hdc_window );\r
2030 \r
2031         if( hPieceFont != NULL ) {\r
2032             DeleteObject( hPieceFont );\r
2033         }\r
2034         else {\r
2035             for( i=0; i<=(int)BlackKing; i++ ) {\r
2036                 hPieceMask[i] = NULL;\r
2037                 hPieceFace[i] = NULL;\r
2038             }\r
2039         }\r
2040 \r
2041         fontHeight = 75;\r
2042 \r
2043         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2044             fontHeight = appData.fontPieceSize;\r
2045         }\r
2046 \r
2047         fontHeight = (fontHeight * squareSize) / 100;\r
2048 \r
2049         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2050         lf.lfWidth = 0;\r
2051         lf.lfEscapement = 0;\r
2052         lf.lfOrientation = 0;\r
2053         lf.lfWeight = FW_NORMAL;\r
2054         lf.lfItalic = 0;\r
2055         lf.lfUnderline = 0;\r
2056         lf.lfStrikeOut = 0;\r
2057         lf.lfCharSet = DEFAULT_CHARSET;\r
2058         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2059         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2060         lf.lfQuality = PROOF_QUALITY;\r
2061         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2062         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2063         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2064 \r
2065         hPieceFont = CreateFontIndirect( &lf );\r
2066 \r
2067         if( hPieceFont == NULL ) {\r
2068             fontBitmapSquareSize = -2;\r
2069         }\r
2070         else {\r
2071             /* Setup font-to-piece character table */\r
2072             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2073                 /* No (or wrong) global settings, try to detect the font */\r
2074                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2075                     /* Alpha */\r
2076                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2077                 }\r
2078                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2079                     /* DiagramTT* family */\r
2080                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2081                 }\r
2082                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2083                     /* Fairy symbols */\r
2084                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2085                 }\r
2086                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2087                     /* Good Companion (Some characters get warped as literal :-( */\r
2088                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2089                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2090                     SetCharTable(pieceToFontChar, s);\r
2091                 }\r
2092                 else {\r
2093                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2094                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2095                 }\r
2096             }\r
2097 \r
2098             /* Create bitmaps */\r
2099             hfont_old = SelectObject( hdc, hPieceFont );\r
2100             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2101                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2102                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2103 \r
2104             SelectObject( hdc, hfont_old );\r
2105 \r
2106             fontBitmapSquareSize = squareSize;\r
2107         }\r
2108     }\r
2109 \r
2110     if( hdc != NULL ) {\r
2111         DeleteDC( hdc );\r
2112     }\r
2113 \r
2114     if( hdc_window != NULL ) {\r
2115         ReleaseDC( hwndMain, hdc_window );\r
2116     }\r
2117 }\r
2118 \r
2119 HBITMAP\r
2120 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2121 {\r
2122   char name[128], buf[MSG_SIZ];\r
2123 \r
2124     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2125   if(appData.pieceDirectory[0]) {\r
2126     HBITMAP res;\r
2127     snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);\r
2128     res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
2129     if(res) return res;\r
2130   }\r
2131   if (gameInfo.event &&\r
2132       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2133       strcmp(name, "k80s") == 0) {\r
2134     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2135   }\r
2136   return LoadBitmap(hinst, name);\r
2137 }\r
2138 \r
2139 \r
2140 /* Insert a color into the program's logical palette\r
2141    structure.  This code assumes the given color is\r
2142    the result of the RGB or PALETTERGB macro, and it\r
2143    knows how those macros work (which is documented).\r
2144 */\r
2145 VOID\r
2146 InsertInPalette(COLORREF color)\r
2147 {\r
2148   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2149 \r
2150   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2151     DisplayFatalError(_("Too many colors"), 0, 1);\r
2152     pLogPal->palNumEntries--;\r
2153     return;\r
2154   }\r
2155 \r
2156   pe->peFlags = (char) 0;\r
2157   pe->peRed = (char) (0xFF & color);\r
2158   pe->peGreen = (char) (0xFF & (color >> 8));\r
2159   pe->peBlue = (char) (0xFF & (color >> 16));\r
2160   return;\r
2161 }\r
2162 \r
2163 \r
2164 VOID\r
2165 InitDrawingColors()\r
2166 {\r
2167   int i;\r
2168   if (pLogPal == NULL) {\r
2169     /* Allocate enough memory for a logical palette with\r
2170      * PALETTESIZE entries and set the size and version fields\r
2171      * of the logical palette structure.\r
2172      */\r
2173     pLogPal = (NPLOGPALETTE)\r
2174       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2175                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2176     pLogPal->palVersion    = 0x300;\r
2177   }\r
2178   pLogPal->palNumEntries = 0;\r
2179 \r
2180   InsertInPalette(lightSquareColor);\r
2181   InsertInPalette(darkSquareColor);\r
2182   InsertInPalette(whitePieceColor);\r
2183   InsertInPalette(blackPieceColor);\r
2184   InsertInPalette(highlightSquareColor);\r
2185   InsertInPalette(premoveHighlightColor);\r
2186 \r
2187   /*  create a logical color palette according the information\r
2188    *  in the LOGPALETTE structure.\r
2189    */\r
2190   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2191 \r
2192   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2193   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2194   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2195   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2196   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2197   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2198   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2199     for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers\r
2200 \r
2201    /* [AS] Force rendering of the font-based pieces */\r
2202   if( fontBitmapSquareSize > 0 ) {\r
2203     fontBitmapSquareSize = 0;\r
2204   }\r
2205 }\r
2206 \r
2207 \r
2208 int\r
2209 BoardWidth(int boardSize, int n)\r
2210 { /* [HGM] argument n added to allow different width and height */\r
2211   int lineGap = sizeInfo[boardSize].lineGap;\r
2212 \r
2213   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2214       lineGap = appData.overrideLineGap;\r
2215   }\r
2216 \r
2217   return (n + 1) * lineGap +\r
2218           n * sizeInfo[boardSize].squareSize;\r
2219 }\r
2220 \r
2221 /* Respond to board resize by dragging edge */\r
2222 VOID\r
2223 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2224 {\r
2225   BoardSize newSize = NUM_SIZES - 1;\r
2226   static int recurse = 0;\r
2227   if (IsIconic(hwndMain)) return;\r
2228   if (recurse > 0) return;\r
2229   recurse++;\r
2230   while (newSize > 0) {\r
2231         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2232         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2233            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2234     newSize--;\r
2235   } \r
2236   boardSize = newSize;\r
2237   InitDrawingSizes(boardSize, flags);\r
2238   recurse--;\r
2239 }\r
2240 \r
2241 \r
2242 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2243 \r
2244 VOID\r
2245 InitDrawingSizes(BoardSize boardSize, int flags)\r
2246 {\r
2247   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2248   ChessSquare piece;\r
2249   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2250   HDC hdc;\r
2251   SIZE clockSize, messageSize;\r
2252   HFONT oldFont;\r
2253   char buf[MSG_SIZ];\r
2254   char *str;\r
2255   HMENU hmenu = GetMenu(hwndMain);\r
2256   RECT crect, wrect, oldRect;\r
2257   int offby;\r
2258   LOGBRUSH logbrush;\r
2259   VariantClass v = gameInfo.variant;\r
2260 \r
2261   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2262   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2263 \r
2264   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2265   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2266   if(boardSize == -1) return;     // no size defined yet; abort (to allow early call of InitPosition)\r
2267   oldBoardSize = boardSize;\r
2268 \r
2269   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2270   { // correct board size to one where built-in pieces exist\r
2271     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2272        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2273       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2274       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2275       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy || v == VariantLion ) {\r
2276       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2277       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2278                                    boardSize = SizeMiddling;\r
2279     }\r
2280   }\r
2281   if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2282 \r
2283   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2284   oldRect.top = wpMain.y;\r
2285   oldRect.right = wpMain.x + wpMain.width;\r
2286   oldRect.bottom = wpMain.y + wpMain.height;\r
2287 \r
2288   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2289   smallLayout = sizeInfo[boardSize].smallLayout;\r
2290   squareSize = sizeInfo[boardSize].squareSize;\r
2291   lineGap = sizeInfo[boardSize].lineGap;\r
2292   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2293   border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;\r
2294 \r
2295   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2296       lineGap = appData.overrideLineGap;\r
2297   }\r
2298 \r
2299   if (tinyLayout != oldTinyLayout) {\r
2300     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2301     if (tinyLayout) {\r
2302       style &= ~WS_SYSMENU;\r
2303       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2304                  "&Minimize\tCtrl+F4");\r
2305     } else {\r
2306       style |= WS_SYSMENU;\r
2307       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2308     }\r
2309     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2310 \r
2311     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2312       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2313         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2314     }\r
2315     DrawMenuBar(hwndMain);\r
2316   }\r
2317 \r
2318   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;\r
2319   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;\r
2320 \r
2321   /* Get text area sizes */\r
2322   hdc = GetDC(hwndMain);\r
2323   if (appData.clockMode) {\r
2324     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2325   } else {\r
2326     snprintf(buf, MSG_SIZ, _("White"));\r
2327   }\r
2328   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2329   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2330   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2331   str = _("We only care about the height here");\r
2332   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2333   SelectObject(hdc, oldFont);\r
2334   ReleaseDC(hwndMain, hdc);\r
2335 \r
2336   /* Compute where everything goes */\r
2337   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2338         /* [HGM] logo: if either logo is on, reserve space for it */\r
2339         logoHeight =  2*clockSize.cy;\r
2340         leftLogoRect.left   = OUTER_MARGIN;\r
2341         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2342         leftLogoRect.top    = OUTER_MARGIN;\r
2343         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2344 \r
2345         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2346         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2347         rightLogoRect.top    = OUTER_MARGIN;\r
2348         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2349 \r
2350 \r
2351     whiteRect.left = leftLogoRect.right;\r
2352     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2353     whiteRect.top = OUTER_MARGIN;\r
2354     whiteRect.bottom = whiteRect.top + logoHeight;\r
2355 \r
2356     blackRect.right = rightLogoRect.left;\r
2357     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2358     blackRect.top = whiteRect.top;\r
2359     blackRect.bottom = whiteRect.bottom;\r
2360   } else {\r
2361     whiteRect.left = OUTER_MARGIN;\r
2362     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2363     whiteRect.top = OUTER_MARGIN;\r
2364     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2365 \r
2366     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2367     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2368     blackRect.top = whiteRect.top;\r
2369     blackRect.bottom = whiteRect.bottom;\r
2370 \r
2371     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2372   }\r
2373 \r
2374   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2375   if (appData.showButtonBar) {\r
2376     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2377       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2378   } else {\r
2379     messageRect.right = OUTER_MARGIN + boardWidth;\r
2380   }\r
2381   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2382   messageRect.bottom = messageRect.top + messageSize.cy;\r
2383 \r
2384   boardRect.left = OUTER_MARGIN;\r
2385   boardRect.right = boardRect.left + boardWidth;\r
2386   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2387   boardRect.bottom = boardRect.top + boardHeight;\r
2388 \r
2389   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2390   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2391   oldTinyLayout = tinyLayout;\r
2392   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2393   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2394     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2395   winW *= 1 + twoBoards;\r
2396   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2397   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2398   wpMain.height = winH; //       without disturbing window attachments\r
2399   GetWindowRect(hwndMain, &wrect);\r
2400   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2401                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2402 \r
2403   // [HGM] placement: let attached windows follow size change.\r
2404   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2405   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2406   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2407   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2408   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2409 \r
2410   /* compensate if menu bar wrapped */\r
2411   GetClientRect(hwndMain, &crect);\r
2412   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2413   wpMain.height += offby;\r
2414   switch (flags) {\r
2415   case WMSZ_TOPLEFT:\r
2416     SetWindowPos(hwndMain, NULL, \r
2417                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2418                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2419     break;\r
2420 \r
2421   case WMSZ_TOPRIGHT:\r
2422   case WMSZ_TOP:\r
2423     SetWindowPos(hwndMain, NULL, \r
2424                  wrect.left, wrect.bottom - wpMain.height, \r
2425                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2426     break;\r
2427 \r
2428   case WMSZ_BOTTOMLEFT:\r
2429   case WMSZ_LEFT:\r
2430     SetWindowPos(hwndMain, NULL, \r
2431                  wrect.right - wpMain.width, wrect.top, \r
2432                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2433     break;\r
2434 \r
2435   case WMSZ_BOTTOMRIGHT:\r
2436   case WMSZ_BOTTOM:\r
2437   case WMSZ_RIGHT:\r
2438   default:\r
2439     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2440                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2441     break;\r
2442   }\r
2443 \r
2444   hwndPause = NULL;\r
2445   for (i = 0; i < N_BUTTONS; i++) {\r
2446     if (buttonDesc[i].hwnd != NULL) {\r
2447       DestroyWindow(buttonDesc[i].hwnd);\r
2448       buttonDesc[i].hwnd = NULL;\r
2449     }\r
2450     if (appData.showButtonBar) {\r
2451       buttonDesc[i].hwnd =\r
2452         CreateWindow("BUTTON", buttonDesc[i].label,\r
2453                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2454                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2455                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2456                      (HMENU) buttonDesc[i].id,\r
2457                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2458       if (tinyLayout) {\r
2459         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2460                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2461                     MAKELPARAM(FALSE, 0));\r
2462       }\r
2463       if (buttonDesc[i].id == IDM_Pause)\r
2464         hwndPause = buttonDesc[i].hwnd;\r
2465       buttonDesc[i].wndproc = (WNDPROC)\r
2466         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2467     }\r
2468   }\r
2469   if (gridPen != NULL) DeleteObject(gridPen);\r
2470   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2471   if (premovePen != NULL) DeleteObject(premovePen);\r
2472   if (lineGap != 0) {\r
2473     logbrush.lbStyle = BS_SOLID;\r
2474     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2475     gridPen =\r
2476       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2477                    lineGap, &logbrush, 0, NULL);\r
2478     logbrush.lbColor = highlightSquareColor;\r
2479     highlightPen =\r
2480       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2481                    lineGap, &logbrush, 0, NULL);\r
2482 \r
2483     logbrush.lbColor = premoveHighlightColor; \r
2484     premovePen =\r
2485       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2486                    lineGap, &logbrush, 0, NULL);\r
2487 \r
2488     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2489     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2490       gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;\r
2491       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2492         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2493       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2494         BOARD_WIDTH * (squareSize + lineGap) + border;\r
2495       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2496     }\r
2497     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2498       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;\r
2499       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2500         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2501         lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2502       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2503         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;\r
2504       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2505     }\r
2506   }\r
2507 \r
2508   /* [HGM] Licensing requirement */\r
2509 #ifdef GOTHIC\r
2510   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2511 #endif\r
2512 #ifdef FALCON\r
2513   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2514 #endif\r
2515   GothicPopUp( "", VariantNormal);\r
2516 \r
2517 \r
2518 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2519 \r
2520   /* Load piece bitmaps for this board size */\r
2521   for (i=0; i<=2; i++) {\r
2522     for (piece = WhitePawn;\r
2523          (int) piece < (int) BlackPawn;\r
2524          piece = (ChessSquare) ((int) piece + 1)) {\r
2525       if (pieceBitmap[i][piece] != NULL)\r
2526         DeleteObject(pieceBitmap[i][piece]);\r
2527     }\r
2528   }\r
2529 \r
2530   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2531   // Orthodox Chess pieces\r
2532   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2533   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2534   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2535   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2536   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2537   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2538   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2539   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2540   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2541   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2542   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2543   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2544   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2545   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2546   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2547   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2548     // in Shogi, Hijack the unused Queen for Lance\r
2549     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2550     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2551     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2552   } else {\r
2553     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2554     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2555     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2556   }\r
2557 \r
2558   if(squareSize <= 72 && squareSize >= 33) { \r
2559     /* A & C are available in most sizes now */\r
2560     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2561       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2562       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2563       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2564       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2565       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2566       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2567       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2568       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2569       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2570       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2571       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2572       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2573     } else { // Smirf-like\r
2574       if(gameInfo.variant == VariantSChess) {\r
2575         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2576         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2577         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2578       } else {\r
2579         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2580         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2581         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2582       }\r
2583     }\r
2584     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2585       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2586       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2587       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2588     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2589       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2590       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2591       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2592     } else { // WinBoard standard\r
2593       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2594       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2595       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2596     }\r
2597   }\r
2598 \r
2599 \r
2600   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2601     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2602     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2603     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2604     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2605     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2606     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2607     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2608     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2609     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2610     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2611     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2612     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2613     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2614     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2615     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2616     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2617     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2618     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2619     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2620     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2621     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2622     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2623     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2624     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2625     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2626     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2627     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2628     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2629     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2630     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2631     pieceBitmap[0][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "s");\r
2632     pieceBitmap[1][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "o");\r
2633     pieceBitmap[2][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "w");\r
2634 \r
2635     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2636       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2637       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2638       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2639       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2640       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2641       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2642       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2643       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2644       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2645       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2646       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2647       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2648     } else {\r
2649       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2650       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2651       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2652       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2653       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2654       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2655       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2656       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2657       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2658       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2659       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2660       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2661     }\r
2662 \r
2663   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2664     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2665     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2666     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2667     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2668     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2669     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2670     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2671     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2672     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2673     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2674     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2675     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2676     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2677     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2678   }\r
2679 \r
2680 \r
2681   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2682   /* special Shogi support in this size */\r
2683   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2684       for (piece = WhitePawn;\r
2685            (int) piece < (int) BlackPawn;\r
2686            piece = (ChessSquare) ((int) piece + 1)) {\r
2687         if (pieceBitmap[i][piece] != NULL)\r
2688           DeleteObject(pieceBitmap[i][piece]);\r
2689       }\r
2690     }\r
2691   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2692   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2693   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2694   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2695   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2696   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2697   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2698   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2699   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2700   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2701   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2702   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2703   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2704   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2705   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2706   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2707   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2708   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2709   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2710   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2711   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2712   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2713   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2714   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2715   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2716   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2717   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2718   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2719   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2720   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2721   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2722   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2723   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2724   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2725   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2726   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2727   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2728   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2729   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2730   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2731   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2732   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2733   minorSize = 0;\r
2734   }\r
2735 }\r
2736 \r
2737 HBITMAP\r
2738 PieceBitmap(ChessSquare p, int kind)\r
2739 {\r
2740   if ((int) p >= (int) BlackPawn)\r
2741     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2742 \r
2743   return pieceBitmap[kind][(int) p];\r
2744 }\r
2745 \r
2746 /***************************************************************/\r
2747 \r
2748 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2749 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2750 /*\r
2751 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2752 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2753 */\r
2754 \r
2755 VOID\r
2756 SquareToPos(int row, int column, int * x, int * y)\r
2757 {\r
2758   if (flipView) {\r
2759     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
2760     *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;\r
2761   } else {\r
2762     *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;\r
2763     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
2764   }\r
2765 }\r
2766 \r
2767 VOID\r
2768 DrawCoordsOnDC(HDC hdc)\r
2769 {\r
2770   static char files[] = "0123456789012345678901221098765432109876543210";\r
2771   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2772   char str[2] = { NULLCHAR, NULLCHAR };\r
2773   int oldMode, oldAlign, x, y, start, i;\r
2774   HFONT oldFont;\r
2775   HBRUSH oldBrush;\r
2776 \r
2777   if (!appData.showCoords)\r
2778     return;\r
2779 \r
2780   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2781 \r
2782   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2783   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2784   oldAlign = GetTextAlign(hdc);\r
2785   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2786 \r
2787   y = boardRect.top + lineGap;\r
2788   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2789 \r
2790   if(border) {\r
2791     SetTextAlign(hdc, TA_RIGHT|TA_TOP);\r
2792     x += border - lineGap - 4; y += squareSize - 6;\r
2793   } else\r
2794   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2795   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2796     str[0] = files[start + i];\r
2797     ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);\r
2798     y += squareSize + lineGap;\r
2799   }\r
2800 \r
2801   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2802 \r
2803   if(border) {\r
2804     SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2805     x += -border + 4; y += border - squareSize + 6;\r
2806   } else\r
2807   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2808   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2809     str[0] = ranks[start + i];\r
2810     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2811     x += squareSize + lineGap;\r
2812   }    \r
2813 \r
2814   SelectObject(hdc, oldBrush);\r
2815   SetBkMode(hdc, oldMode);\r
2816   SetTextAlign(hdc, oldAlign);\r
2817   SelectObject(hdc, oldFont);\r
2818 }\r
2819 \r
2820 VOID\r
2821 DrawGridOnDC(HDC hdc)\r
2822 {\r
2823   HPEN oldPen;\r
2824  \r
2825   if (lineGap != 0) {\r
2826     oldPen = SelectObject(hdc, gridPen);\r
2827     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2828     SelectObject(hdc, oldPen);\r
2829   }\r
2830 }\r
2831 \r
2832 #define HIGHLIGHT_PEN 0\r
2833 #define PREMOVE_PEN   1\r
2834 \r
2835 VOID\r
2836 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2837 {\r
2838   int x1, y1;\r
2839   HPEN oldPen, hPen;\r
2840   if (lineGap == 0) return;\r
2841   if (flipView) {\r
2842     x1 = boardRect.left +\r
2843       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;\r
2844     y1 = boardRect.top +\r
2845       lineGap/2 + y * (squareSize + lineGap) + border;\r
2846   } else {\r
2847     x1 = boardRect.left +\r
2848       lineGap/2 + x * (squareSize + lineGap) + border;\r
2849     y1 = boardRect.top +\r
2850       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;\r
2851   }\r
2852   hPen = pen ? premovePen : highlightPen;\r
2853   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2854   MoveToEx(hdc, x1, y1, NULL);\r
2855   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2856   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2857   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2858   LineTo(hdc, x1, y1);\r
2859   SelectObject(hdc, oldPen);\r
2860 }\r
2861 \r
2862 VOID\r
2863 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2864 {\r
2865   int i;\r
2866   for (i=0; i<2; i++) {\r
2867     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2868       DrawHighlightOnDC(hdc, TRUE,\r
2869                         h->sq[i].x, h->sq[i].y,\r
2870                         pen);\r
2871   }\r
2872 }\r
2873 \r
2874 /* Note: sqcolor is used only in monoMode */\r
2875 /* Note that this code is largely duplicated in woptions.c,\r
2876    function DrawSampleSquare, so that needs to be updated too */\r
2877 VOID\r
2878 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2879 {\r
2880   HBITMAP oldBitmap;\r
2881   HBRUSH oldBrush;\r
2882   int tmpSize;\r
2883 \r
2884   if (appData.blindfold) return;\r
2885 \r
2886   /* [AS] Use font-based pieces if needed */\r
2887   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2888     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2889     CreatePiecesFromFont();\r
2890 \r
2891     if( fontBitmapSquareSize == squareSize ) {\r
2892         int index = TranslatePieceToFontPiece(piece);\r
2893 \r
2894         SelectObject( tmphdc, hPieceMask[ index ] );\r
2895 \r
2896       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2897         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2898       else\r
2899         BitBlt( hdc,\r
2900             x, y,\r
2901             squareSize, squareSize,\r
2902             tmphdc,\r
2903             0, 0,\r
2904             SRCAND );\r
2905 \r
2906         SelectObject( tmphdc, hPieceFace[ index ] );\r
2907 \r
2908       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2909         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2910       else\r
2911         BitBlt( hdc,\r
2912             x, y,\r
2913             squareSize, squareSize,\r
2914             tmphdc,\r
2915             0, 0,\r
2916             SRCPAINT );\r
2917 \r
2918         return;\r
2919     }\r
2920   }\r
2921 \r
2922   if (appData.monoMode) {\r
2923     SelectObject(tmphdc, PieceBitmap(piece, \r
2924       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2925     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2926            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2927   } else {\r
2928     HBRUSH xBrush = whitePieceBrush;\r
2929     tmpSize = squareSize;\r
2930     if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);\r
2931     if(minorSize &&\r
2932         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2933          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2934       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2935       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2936       x += (squareSize - minorSize)>>1;\r
2937       y += squareSize - minorSize - 2;\r
2938       tmpSize = minorSize;\r
2939     }\r
2940     if (color || appData.allWhite ) {\r
2941       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2942       if( color )\r
2943               oldBrush = SelectObject(hdc, xBrush);\r
2944       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2945       if(appData.upsideDown && color==flipView)\r
2946         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2947       else\r
2948         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2949       /* Use black for outline of white pieces */\r
2950       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2951       if(appData.upsideDown && color==flipView)\r
2952         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2953       else\r
2954         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2955     } else if(appData.pieceDirectory[0]) {\r
2956       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2957       oldBrush = SelectObject(hdc, xBrush);\r
2958       if(appData.upsideDown && color==flipView)\r
2959         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2960       else\r
2961         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2962       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2963       if(appData.upsideDown && color==flipView)\r
2964         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2965       else\r
2966         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2967     } else {\r
2968       /* Use square color for details of black pieces */\r
2969       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2970       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2971       if(appData.upsideDown && !flipView)\r
2972         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2973       else\r
2974         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2975     }\r
2976     SelectObject(hdc, oldBrush);\r
2977     SelectObject(tmphdc, oldBitmap);\r
2978   }\r
2979 }\r
2980 \r
2981 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2982 int GetBackTextureMode( int algo )\r
2983 {\r
2984     int result = BACK_TEXTURE_MODE_DISABLED;\r
2985 \r
2986     switch( algo ) \r
2987     {\r
2988         case BACK_TEXTURE_MODE_PLAIN:\r
2989             result = 1; /* Always use identity map */\r
2990             break;\r
2991         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2992             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2993             break;\r
2994     }\r
2995 \r
2996     return result;\r
2997 }\r
2998 \r
2999 /* \r
3000     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3001     to handle redraws cleanly (as random numbers would always be different).\r
3002 */\r
3003 VOID RebuildTextureSquareInfo()\r
3004 {\r
3005     BITMAP bi;\r
3006     int lite_w = 0;\r
3007     int lite_h = 0;\r
3008     int dark_w = 0;\r
3009     int dark_h = 0;\r
3010     int row;\r
3011     int col;\r
3012 \r
3013     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3014 \r
3015     if( liteBackTexture != NULL ) {\r
3016         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3017             lite_w = bi.bmWidth;\r
3018             lite_h = bi.bmHeight;\r
3019         }\r
3020     }\r
3021 \r
3022     if( darkBackTexture != NULL ) {\r
3023         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3024             dark_w = bi.bmWidth;\r
3025             dark_h = bi.bmHeight;\r
3026         }\r
3027     }\r
3028 \r
3029     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3030         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3031             if( (col + row) & 1 ) {\r
3032                 /* Lite square */\r
3033                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3034                   if( lite_w >= squareSize*BOARD_WIDTH )\r
3035                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
3036                   else\r
3037                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3038                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
3039                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
3040                   else\r
3041                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3042                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3043                 }\r
3044             }\r
3045             else {\r
3046                 /* Dark square */\r
3047                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3048                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3049                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3050                   else\r
3051                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3052                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3053                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3054                   else\r
3055                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3056                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3057                 }\r
3058             }\r
3059         }\r
3060     }\r
3061 }\r
3062 \r
3063 /* [AS] Arrow highlighting support */\r
3064 \r
3065 static double A_WIDTH = 5; /* Width of arrow body */\r
3066 \r
3067 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3068 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3069 \r
3070 static double Sqr( double x )\r
3071 {\r
3072     return x*x;\r
3073 }\r
3074 \r
3075 static int Round( double x )\r
3076 {\r
3077     return (int) (x + 0.5);\r
3078 }\r
3079 \r
3080 /* Draw an arrow between two points using current settings */\r
3081 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3082 {\r
3083     POINT arrow[7];\r
3084     double dx, dy, j, k, x, y;\r
3085 \r
3086     if( d_x == s_x ) {\r
3087         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3088 \r
3089         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3090         arrow[0].y = s_y;\r
3091 \r
3092         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3093         arrow[1].y = d_y - h;\r
3094 \r
3095         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3096         arrow[2].y = d_y - h;\r
3097 \r
3098         arrow[3].x = d_x;\r
3099         arrow[3].y = d_y;\r
3100 \r
3101         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3102         arrow[5].y = d_y - h;\r
3103 \r
3104         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3105         arrow[4].y = d_y - h;\r
3106 \r
3107         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3108         arrow[6].y = s_y;\r
3109     }\r
3110     else if( d_y == s_y ) {\r
3111         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3112 \r
3113         arrow[0].x = s_x;\r
3114         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3115 \r
3116         arrow[1].x = d_x - w;\r
3117         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3118 \r
3119         arrow[2].x = d_x - w;\r
3120         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3121 \r
3122         arrow[3].x = d_x;\r
3123         arrow[3].y = d_y;\r
3124 \r
3125         arrow[5].x = d_x - w;\r
3126         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3127 \r
3128         arrow[4].x = d_x - w;\r
3129         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3130 \r
3131         arrow[6].x = s_x;\r
3132         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3133     }\r
3134     else {\r
3135         /* [AS] Needed a lot of paper for this! :-) */\r
3136         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3137         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3138   \r
3139         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3140 \r
3141         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3142 \r
3143         x = s_x;\r
3144         y = s_y;\r
3145 \r
3146         arrow[0].x = Round(x - j);\r
3147         arrow[0].y = Round(y + j*dx);\r
3148 \r
3149         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3150         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3151 \r
3152         if( d_x > s_x ) {\r
3153             x = (double) d_x - k;\r
3154             y = (double) d_y - k*dy;\r
3155         }\r
3156         else {\r
3157             x = (double) d_x + k;\r
3158             y = (double) d_y + k*dy;\r
3159         }\r
3160 \r
3161         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3162 \r
3163         arrow[6].x = Round(x - j);\r
3164         arrow[6].y = Round(y + j*dx);\r
3165 \r
3166         arrow[2].x = Round(arrow[6].x + 2*j);\r
3167         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3168 \r
3169         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3170         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3171 \r
3172         arrow[4].x = d_x;\r
3173         arrow[4].y = d_y;\r
3174 \r
3175         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3176         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3177     }\r
3178 \r
3179     Polygon( hdc, arrow, 7 );\r
3180 }\r
3181 \r
3182 /* [AS] Draw an arrow between two squares */\r
3183 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3184 {\r
3185     int s_x, s_y, d_x, d_y;\r
3186     HPEN hpen;\r
3187     HPEN holdpen;\r
3188     HBRUSH hbrush;\r
3189     HBRUSH holdbrush;\r
3190     LOGBRUSH stLB;\r
3191 \r
3192     if( s_col == d_col && s_row == d_row ) {\r
3193         return;\r
3194     }\r
3195 \r
3196     /* Get source and destination points */\r
3197     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3198     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3199 \r
3200     if( d_y > s_y ) {\r
3201         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3202     }\r
3203     else if( d_y < s_y ) {\r
3204         d_y += squareSize / 2 + squareSize / 4;\r
3205     }\r
3206     else {\r
3207         d_y += squareSize / 2;\r
3208     }\r
3209 \r
3210     if( d_x > s_x ) {\r
3211         d_x += squareSize / 2 - squareSize / 4;\r
3212     }\r
3213     else if( d_x < s_x ) {\r
3214         d_x += squareSize / 2 + squareSize / 4;\r
3215     }\r
3216     else {\r
3217         d_x += squareSize / 2;\r
3218     }\r
3219 \r
3220     s_x += squareSize / 2;\r
3221     s_y += squareSize / 2;\r
3222 \r
3223     /* Adjust width */\r
3224     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3225 \r
3226     /* Draw */\r
3227     stLB.lbStyle = BS_SOLID;\r
3228     stLB.lbColor = appData.highlightArrowColor;\r
3229     stLB.lbHatch = 0;\r
3230 \r
3231     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3232     holdpen = SelectObject( hdc, hpen );\r
3233     hbrush = CreateBrushIndirect( &stLB );\r
3234     holdbrush = SelectObject( hdc, hbrush );\r
3235 \r
3236     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3237 \r
3238     SelectObject( hdc, holdpen );\r
3239     SelectObject( hdc, holdbrush );\r
3240     DeleteObject( hpen );\r
3241     DeleteObject( hbrush );\r
3242 }\r
3243 \r
3244 BOOL HasHighlightInfo()\r
3245 {\r
3246     BOOL result = FALSE;\r
3247 \r
3248     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3249         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3250     {\r
3251         result = TRUE;\r
3252     }\r
3253 \r
3254     return result;\r
3255 \r
3256 \r
3257 \r
3258 }\r
3259 \r
3260 BOOL IsDrawArrowEnabled()\r
3261 {\r
3262     BOOL result = FALSE;\r
3263 \r
3264     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3265         result = TRUE;\r
3266     }\r
3267 \r
3268     return result;\r
3269 }\r
3270 \r
3271 VOID DrawArrowHighlight( HDC hdc )\r
3272 {\r
3273     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3274         DrawArrowBetweenSquares( hdc,\r
3275             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3276             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3277     }\r
3278 }\r
3279 \r
3280 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3281 {\r
3282     HRGN result = NULL;\r
3283 \r
3284     if( HasHighlightInfo() ) {\r
3285         int x1, y1, x2, y2;\r
3286         int sx, sy, dx, dy;\r
3287 \r
3288         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3289         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3290 \r
3291         sx = MIN( x1, x2 );\r
3292         sy = MIN( y1, y2 );\r
3293         dx = MAX( x1, x2 ) + squareSize;\r
3294         dy = MAX( y1, y2 ) + squareSize;\r
3295 \r
3296         result = CreateRectRgn( sx, sy, dx, dy );\r
3297     }\r
3298 \r
3299     return result;\r
3300 }\r
3301 \r
3302 /*\r
3303     Warning: this function modifies the behavior of several other functions. \r
3304     \r
3305     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3306     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3307     repaint is scattered all over the place, which is not good for features such as\r
3308     "arrow highlighting" that require a full repaint of the board.\r
3309 \r
3310     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3311     user interaction, when speed is not so important) but especially to avoid errors\r
3312     in the displayed graphics.\r
3313 \r
3314     In such patched places, I always try refer to this function so there is a single\r
3315     place to maintain knowledge.\r
3316     \r
3317     To restore the original behavior, just return FALSE unconditionally.\r
3318 */\r
3319 BOOL IsFullRepaintPreferrable()\r
3320 {\r
3321     BOOL result = FALSE;\r
3322 \r
3323     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3324         /* Arrow may appear on the board */\r
3325         result = TRUE;\r
3326     }\r
3327 \r
3328     return result;\r
3329 }\r
3330 \r
3331 /* \r
3332     This function is called by DrawPosition to know whether a full repaint must\r
3333     be forced or not.\r
3334 \r
3335     Only DrawPosition may directly call this function, which makes use of \r
3336     some state information. Other function should call DrawPosition specifying \r
3337     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3338 */\r
3339 BOOL DrawPositionNeedsFullRepaint()\r
3340 {\r
3341     BOOL result = FALSE;\r
3342 \r
3343     /* \r
3344         Probably a slightly better policy would be to trigger a full repaint\r
3345         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3346         but animation is fast enough that it's difficult to notice.\r
3347     */\r
3348     if( animInfo.piece == EmptySquare ) {\r
3349         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3350             result = TRUE;\r
3351         }\r
3352     }\r
3353 \r
3354     return result;\r
3355 }\r
3356 \r
3357 static HBITMAP borderBitmap;\r
3358 \r
3359 VOID\r
3360 DrawBackgroundOnDC(HDC hdc)\r
3361 {\r
3362   \r
3363   BITMAP bi;\r
3364   HDC tmphdc;\r
3365   HBITMAP hbm;\r
3366   static char oldBorder[MSG_SIZ];\r
3367   int w = 600, h = 600, mode;\r
3368 \r
3369   if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid\r
3370     strncpy(oldBorder, appData.border, MSG_SIZ-1);\r
3371     borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
3372   }\r
3373   if(borderBitmap == NULL) { // loading failed, use white\r
3374     FillRect( hdc, &boardRect, whitePieceBrush );\r
3375     return;\r
3376   }\r
3377   tmphdc = CreateCompatibleDC(hdc);\r
3378   hbm = SelectObject(tmphdc, borderBitmap);\r
3379   if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {\r
3380             w = bi.bmWidth;\r
3381             h = bi.bmHeight;\r
3382   }\r
3383   mode = SetStretchBltMode(hdc, COLORONCOLOR);\r
3384   StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left, \r
3385                   boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3386   SetStretchBltMode(hdc, mode);\r
3387   SelectObject(tmphdc, hbm);\r
3388   DeleteDC(tmphdc);\r
3389 }\r
3390 \r
3391 VOID\r
3392 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3393 {\r
3394   int row, column, x, y, square_color, piece_color;\r
3395   ChessSquare piece;\r
3396   HBRUSH oldBrush;\r
3397   HDC texture_hdc = NULL;\r
3398 \r
3399   /* [AS] Initialize background textures if needed */\r
3400   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3401       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3402       if( backTextureSquareSize != squareSize \r
3403        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3404           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3405           backTextureSquareSize = squareSize;\r
3406           RebuildTextureSquareInfo();\r
3407       }\r
3408 \r
3409       texture_hdc = CreateCompatibleDC( hdc );\r
3410   }\r
3411 \r
3412   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3413     for (column = 0; column < BOARD_WIDTH; column++) {\r
3414   \r
3415       SquareToPos(row, column, &x, &y);\r
3416 \r
3417       piece = board[row][column];\r
3418 \r
3419       square_color = ((column + row) % 2) == 1;\r
3420       if( gameInfo.variant == VariantXiangqi ) {\r
3421           square_color = !InPalace(row, column);\r
3422           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3423           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3424       }\r
3425       piece_color = (int) piece < (int) BlackPawn;\r
3426 \r
3427 \r
3428       /* [HGM] holdings file: light square or black */\r
3429       if(column == BOARD_LEFT-2) {\r
3430             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3431                 square_color = 1;\r
3432             else {\r
3433                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3434                 continue;\r
3435             }\r
3436       } else\r
3437       if(column == BOARD_RGHT + 1 ) {\r
3438             if( row < gameInfo.holdingsSize )\r
3439                 square_color = 1;\r
3440             else {\r
3441                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3442                 continue;\r
3443             }\r
3444       }\r
3445       if(column == BOARD_LEFT-1 ) /* left align */\r
3446             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3447       else if( column == BOARD_RGHT) /* right align */\r
3448             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3449       else if( piece == DarkSquare) DisplayHoldingsCount(hdc, x, y, 0, 0);\r
3450       else\r
3451       if (appData.monoMode) {\r
3452         if (piece == EmptySquare) {\r
3453           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3454                  square_color ? WHITENESS : BLACKNESS);\r
3455         } else {\r
3456           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3457         }\r
3458       } \r
3459       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3460           /* [AS] Draw the square using a texture bitmap */\r
3461           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3462           int r = row, c = column; // [HGM] do not flip board in flipView\r
3463           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3464 \r
3465           DrawTile( x, y, \r
3466               squareSize, squareSize, \r
3467               hdc, \r
3468               texture_hdc,\r
3469               backTextureSquareInfo[r][c].mode,\r
3470               backTextureSquareInfo[r][c].x,\r
3471               backTextureSquareInfo[r][c].y );\r
3472 \r
3473           SelectObject( texture_hdc, hbm );\r
3474 \r
3475           if (piece != EmptySquare) {\r
3476               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3477           }\r
3478       }\r
3479       else {\r
3480         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3481 \r
3482         oldBrush = SelectObject(hdc, brush );\r
3483         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3484         SelectObject(hdc, oldBrush);\r
3485         if (piece != EmptySquare)\r
3486           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3487       }\r
3488     }\r
3489   }\r
3490 \r
3491   if( texture_hdc != NULL ) {\r
3492     DeleteDC( texture_hdc );\r
3493   }\r
3494 }\r
3495 \r
3496 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3497 void fputDW(FILE *f, int x)\r
3498 {\r
3499         fputc(x     & 255, f);\r
3500         fputc(x>>8  & 255, f);\r
3501         fputc(x>>16 & 255, f);\r
3502         fputc(x>>24 & 255, f);\r
3503 }\r
3504 \r
3505 #define MAX_CLIPS 200   /* more than enough */\r
3506 \r
3507 VOID\r
3508 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3509 {\r
3510 //  HBITMAP bufferBitmap;\r
3511   BITMAP bi;\r
3512 //  RECT Rect;\r
3513   HDC tmphdc;\r
3514   HBITMAP hbm;\r
3515   int w = 100, h = 50;\r
3516 \r
3517   if(logo == NULL) {\r
3518     if(!logoHeight) return;\r
3519     FillRect( hdc, &logoRect, whitePieceBrush );\r
3520   }\r
3521 //  GetClientRect(hwndMain, &Rect);\r
3522 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3523 //                                      Rect.bottom-Rect.top+1);\r
3524   tmphdc = CreateCompatibleDC(hdc);\r
3525   hbm = SelectObject(tmphdc, logo);\r
3526   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3527             w = bi.bmWidth;\r
3528             h = bi.bmHeight;\r
3529   }\r
3530   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3531                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3532   SelectObject(tmphdc, hbm);\r
3533   DeleteDC(tmphdc);\r
3534 }\r
3535 \r
3536 VOID\r
3537 DisplayLogos()\r
3538 {\r
3539   if(logoHeight) {\r
3540         HDC hdc = GetDC(hwndMain);\r
3541         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3542         if(appData.autoLogo) {\r
3543           \r
3544           switch(gameMode) { // pick logos based on game mode\r
3545             case IcsObserving:\r
3546                 whiteLogo = second.programLogo; // ICS logo\r
3547                 blackLogo = second.programLogo;\r
3548             default:\r
3549                 break;\r
3550             case IcsPlayingWhite:\r
3551                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3552                 blackLogo = second.programLogo; // ICS logo\r
3553                 break;\r
3554             case IcsPlayingBlack:\r
3555                 whiteLogo = second.programLogo; // ICS logo\r
3556                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3557                 break;\r
3558             case TwoMachinesPlay:\r
3559                 if(first.twoMachinesColor[0] == 'b') {\r
3560                     whiteLogo = second.programLogo;\r
3561                     blackLogo = first.programLogo;\r
3562                 }\r
3563                 break;\r
3564             case MachinePlaysWhite:\r
3565                 blackLogo = userLogo;\r
3566                 break;\r
3567             case MachinePlaysBlack:\r
3568                 whiteLogo = userLogo;\r
3569                 blackLogo = first.programLogo;\r
3570           }\r
3571         }\r
3572         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3573         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3574         ReleaseDC(hwndMain, hdc);\r
3575   }\r
3576 }\r
3577 \r
3578 void\r
3579 UpdateLogos(int display)\r
3580 { // called after loading new engine(s), in tourney or from menu\r
3581   LoadLogo(&first, 0, FALSE);\r
3582   LoadLogo(&second, 1, appData.icsActive);\r
3583   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3584   if(display) DisplayLogos();\r
3585 }\r
3586 \r
3587 static HDC hdcSeek;\r
3588 \r
3589 // [HGM] seekgraph\r
3590 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3591 {\r
3592     POINT stPt;\r
3593     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3594     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3595     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3596     SelectObject( hdcSeek, hp );\r
3597 }\r
3598 \r
3599 // front-end wrapper for drawing functions to do rectangles\r
3600 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3601 {\r
3602     HPEN hp;\r
3603     RECT rc;\r
3604 \r
3605     if (hdcSeek == NULL) {\r
3606     hdcSeek = GetDC(hwndMain);\r
3607       if (!appData.monoMode) {\r
3608         SelectPalette(hdcSeek, hPal, FALSE);\r
3609         RealizePalette(hdcSeek);\r
3610       }\r
3611     }\r
3612     hp = SelectObject( hdcSeek, gridPen );\r
3613     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3614     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3615     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3616     SelectObject( hdcSeek, hp );\r
3617 }\r
3618 \r
3619 // front-end wrapper for putting text in graph\r
3620 void DrawSeekText(char *buf, int x, int y)\r
3621 {\r
3622         SIZE stSize;\r
3623         SetBkMode( hdcSeek, TRANSPARENT );\r
3624         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3625         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3626 }\r
3627 \r
3628 void DrawSeekDot(int x, int y, int color)\r
3629 {\r
3630         int square = color & 0x80;\r
3631         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3632                         color == 0 ? markerBrush[1] : color == 1 ? darkSquareBrush : explodeBrush);\r
3633         color &= 0x7F;\r
3634         if(square)\r
3635             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3636                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3637         else\r
3638             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3639                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3640             SelectObject(hdcSeek, oldBrush);\r
3641 }\r
3642 \r
3643 void DrawSeekOpen()\r
3644 {\r
3645 }\r
3646 \r
3647 void DrawSeekClose()\r
3648 {\r
3649 }\r
3650 \r
3651 VOID\r
3652 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3653 {\r
3654   static Board lastReq[2], lastDrawn[2];\r
3655   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3656   static int lastDrawnFlipView = 0;\r
3657   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3658   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3659   HDC tmphdc;\r
3660   HDC hdcmem;\r
3661   HBITMAP bufferBitmap;\r
3662   HBITMAP oldBitmap;\r
3663   RECT Rect;\r
3664   HRGN clips[MAX_CLIPS];\r
3665   ChessSquare dragged_piece = EmptySquare;\r
3666   int nr = twoBoards*partnerUp;\r
3667 \r
3668   /* I'm undecided on this - this function figures out whether a full\r
3669    * repaint is necessary on its own, so there's no real reason to have the\r
3670    * caller tell it that.  I think this can safely be set to FALSE - but\r
3671    * if we trust the callers not to request full repaints unnessesarily, then\r
3672    * we could skip some clipping work.  In other words, only request a full\r
3673    * redraw when the majority of pieces have changed positions (ie. flip, \r
3674    * gamestart and similar)  --Hawk\r
3675    */\r
3676   Boolean fullrepaint = repaint;\r
3677 \r
3678   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3679 \r
3680   if( DrawPositionNeedsFullRepaint() ) {\r
3681       fullrepaint = TRUE;\r
3682   }\r
3683 \r
3684   if (board == NULL) {\r
3685     if (!lastReqValid[nr]) {\r
3686       return;\r
3687     }\r
3688     board = lastReq[nr];\r
3689   } else {\r
3690     CopyBoard(lastReq[nr], board);\r
3691     lastReqValid[nr] = 1;\r
3692   }\r
3693 \r
3694   if (doingSizing) {\r
3695     return;\r
3696   }\r
3697 \r
3698   if (IsIconic(hwndMain)) {\r
3699     return;\r
3700   }\r
3701 \r
3702   if (hdc == NULL) {\r
3703     hdc = GetDC(hwndMain);\r
3704     if (!appData.monoMode) {\r
3705       SelectPalette(hdc, hPal, FALSE);\r
3706       RealizePalette(hdc);\r
3707     }\r
3708     releaseDC = TRUE;\r
3709   } else {\r
3710     releaseDC = FALSE;\r
3711   }\r
3712 \r
3713   /* Create some work-DCs */\r
3714   hdcmem = CreateCompatibleDC(hdc);\r
3715   tmphdc = CreateCompatibleDC(hdc);\r
3716 \r
3717   /* If dragging is in progress, we temporarely remove the piece */\r
3718   /* [HGM] or temporarily decrease count if stacked              */\r
3719   /*       !! Moved to before board compare !!                   */\r
3720   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3721     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3722     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3723             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3724         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3725     } else \r
3726     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3727             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3728         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3729     } else \r
3730         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3731   }\r
3732 \r
3733   /* Figure out which squares need updating by comparing the \r
3734    * newest board with the last drawn board and checking if\r
3735    * flipping has changed.\r
3736    */\r
3737   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3738     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3739       for (column = 0; column < BOARD_WIDTH; column++) {\r
3740         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3741           SquareToPos(row, column, &x, &y);\r
3742           clips[num_clips++] =\r
3743             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3744         }\r
3745       }\r
3746     }\r
3747    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3748     for (i=0; i<2; i++) {\r
3749       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3750           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3751         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3752             lastDrawnHighlight.sq[i].y >= 0) {\r
3753           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3754                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3755           clips[num_clips++] =\r
3756             CreateRectRgn(x - lineGap, y - lineGap, \r
3757                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3758         }\r
3759         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3760           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3761           clips[num_clips++] =\r
3762             CreateRectRgn(x - lineGap, y - lineGap, \r
3763                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3764         }\r
3765       }\r
3766     }\r
3767     for (i=0; i<2; i++) {\r
3768       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3769           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3770         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3771             lastDrawnPremove.sq[i].y >= 0) {\r
3772           SquareToPos(lastDrawnPremove.sq[i].y,\r
3773                       lastDrawnPremove.sq[i].x, &x, &y);\r
3774           clips[num_clips++] =\r
3775             CreateRectRgn(x - lineGap, y - lineGap, \r
3776                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3777         }\r
3778         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3779             premoveHighlightInfo.sq[i].y >= 0) {\r
3780           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3781                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3782           clips[num_clips++] =\r
3783             CreateRectRgn(x - lineGap, y - lineGap, \r
3784                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3785         }\r
3786       }\r
3787     }\r
3788    } else { // nr == 1\r
3789         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3790         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3791         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3792         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3793       for (i=0; i<2; i++) {\r
3794         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3795             partnerHighlightInfo.sq[i].y >= 0) {\r
3796           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3797                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3798           clips[num_clips++] =\r
3799             CreateRectRgn(x - lineGap, y - lineGap, \r
3800                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3801         }\r
3802         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3803             oldPartnerHighlight.sq[i].y >= 0) {\r
3804           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3805                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3806           clips[num_clips++] =\r
3807             CreateRectRgn(x - lineGap, y - lineGap, \r
3808                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3809         }\r
3810       }\r
3811    }\r
3812   } else {\r
3813     fullrepaint = TRUE;\r
3814   }\r
3815 \r
3816   /* Create a buffer bitmap - this is the actual bitmap\r
3817    * being written to.  When all the work is done, we can\r
3818    * copy it to the real DC (the screen).  This avoids\r
3819    * the problems with flickering.\r
3820    */\r
3821   GetClientRect(hwndMain, &Rect);\r
3822   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3823                                         Rect.bottom-Rect.top+1);\r
3824   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3825   if (!appData.monoMode) {\r
3826     SelectPalette(hdcmem, hPal, FALSE);\r
3827   }\r
3828 \r
3829   /* Create clips for dragging */\r
3830   if (!fullrepaint) {\r
3831     if (dragInfo.from.x >= 0) {\r
3832       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3833       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3834     }\r
3835     if (dragInfo.start.x >= 0) {\r
3836       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3837       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3838     }\r
3839     if (dragInfo.pos.x >= 0) {\r
3840       x = dragInfo.pos.x - squareSize / 2;\r
3841       y = dragInfo.pos.y - squareSize / 2;\r
3842       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3843     }\r
3844     if (dragInfo.lastpos.x >= 0) {\r
3845       x = dragInfo.lastpos.x - squareSize / 2;\r
3846       y = dragInfo.lastpos.y - squareSize / 2;\r
3847       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3848     }\r
3849   }\r
3850 \r
3851   /* Are we animating a move?  \r
3852    * If so, \r
3853    *   - remove the piece from the board (temporarely)\r
3854    *   - calculate the clipping region\r
3855    */\r
3856   if (!fullrepaint) {\r
3857     if (animInfo.piece != EmptySquare) {\r
3858       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3859       x = boardRect.left + animInfo.lastpos.x;\r
3860       y = boardRect.top + animInfo.lastpos.y;\r
3861       x2 = boardRect.left + animInfo.pos.x;\r
3862       y2 = boardRect.top + animInfo.pos.y;\r
3863       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3864       /* Slight kludge.  The real problem is that after AnimateMove is\r
3865          done, the position on the screen does not match lastDrawn.\r
3866          This currently causes trouble only on e.p. captures in\r
3867          atomic, where the piece moves to an empty square and then\r
3868          explodes.  The old and new positions both had an empty square\r
3869          at the destination, but animation has drawn a piece there and\r
3870          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3871       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3872     }\r
3873   }\r
3874 \r
3875   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3876   if (num_clips == 0)\r
3877     fullrepaint = TRUE;\r
3878 \r
3879   /* Set clipping on the memory DC */\r
3880   if (!fullrepaint) {\r
3881     SelectClipRgn(hdcmem, clips[0]);\r
3882     for (x = 1; x < num_clips; x++) {\r
3883       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3884         abort();  // this should never ever happen!\r
3885     }\r
3886   }\r
3887 \r
3888   /* Do all the drawing to the memory DC */\r
3889   if(explodeInfo.radius) { // [HGM] atomic\r
3890         HBRUSH oldBrush;\r
3891         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3892         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3893         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3894         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3895         x += squareSize/2;\r
3896         y += squareSize/2;\r
3897         if(!fullrepaint) {\r
3898           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3899           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3900         }\r
3901         DrawGridOnDC(hdcmem);\r
3902         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3903         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3904         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3905         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3906         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3907         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3908         SelectObject(hdcmem, oldBrush);\r
3909   } else {\r
3910     if(border) DrawBackgroundOnDC(hdcmem);\r
3911     DrawGridOnDC(hdcmem);\r
3912     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3913         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3914         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3915     } else {\r
3916         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3917         oldPartnerHighlight = partnerHighlightInfo;\r
3918     }\r
3919     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3920   }\r
3921   if(nr == 0) // [HGM] dual: markers only on left board\r
3922   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3923     for (column = 0; column < BOARD_WIDTH; column++) {\r
3924         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3925             HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);\r
3926             SquareToPos(row, column, &x, &y);\r
3927             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3928                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3929             SelectObject(hdcmem, oldBrush);\r
3930         }\r
3931     }\r
3932   }\r
3933 \r
3934   if( appData.highlightMoveWithArrow ) {\r
3935     DrawArrowHighlight(hdcmem);\r
3936   }\r
3937 \r
3938   DrawCoordsOnDC(hdcmem);\r
3939 \r
3940   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3941                  /* to make sure lastDrawn contains what is actually drawn */\r
3942 \r
3943   /* Put the dragged piece back into place and draw it (out of place!) */\r
3944     if (dragged_piece != EmptySquare) {\r
3945     /* [HGM] or restack */\r
3946     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3947                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3948     else\r
3949     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3950                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3951     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3952     x = dragInfo.pos.x - squareSize / 2;\r
3953     y = dragInfo.pos.y - squareSize / 2;\r
3954     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
3955                   ((int) dragInfo.piece < (int) BlackPawn), \r
3956                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3957   }   \r
3958   \r
3959   /* Put the animated piece back into place and draw it */\r
3960   if (animInfo.piece != EmptySquare) {\r
3961     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3962     x = boardRect.left + animInfo.pos.x;\r
3963     y = boardRect.top + animInfo.pos.y;\r
3964     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3965                   ((int) animInfo.piece < (int) BlackPawn),\r
3966                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3967   }\r
3968 \r
3969   /* Release the bufferBitmap by selecting in the old bitmap \r
3970    * and delete the memory DC\r
3971    */\r
3972   SelectObject(hdcmem, oldBitmap);\r
3973   DeleteDC(hdcmem);\r
3974 \r
3975   /* Set clipping on the target DC */\r
3976   if (!fullrepaint) {\r
3977     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3978         RECT rect;\r
3979         GetRgnBox(clips[x], &rect);\r
3980         DeleteObject(clips[x]);\r
3981         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3982                           rect.right + wpMain.width/2, rect.bottom);\r
3983     }\r
3984     SelectClipRgn(hdc, clips[0]);\r
3985     for (x = 1; x < num_clips; x++) {\r
3986       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3987         abort();   // this should never ever happen!\r
3988     } \r
3989   }\r
3990 \r
3991   /* Copy the new bitmap onto the screen in one go.\r
3992    * This way we avoid any flickering\r
3993    */\r
3994   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3995   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3996          boardRect.right - boardRect.left,\r
3997          boardRect.bottom - boardRect.top,\r
3998          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3999   if(saveDiagFlag) { \r
4000     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
4001     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4002 \r
4003     GetObject(bufferBitmap, sizeof(b), &b);\r
4004     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
4005         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4006         bih.biWidth = b.bmWidth;\r
4007         bih.biHeight = b.bmHeight;\r
4008         bih.biPlanes = 1;\r
4009         bih.biBitCount = b.bmBitsPixel;\r
4010         bih.biCompression = 0;\r
4011         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4012         bih.biXPelsPerMeter = 0;\r
4013         bih.biYPelsPerMeter = 0;\r
4014         bih.biClrUsed = 0;\r
4015         bih.biClrImportant = 0;\r
4016 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4017 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4018         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4019 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4020 \r
4021         wb = b.bmWidthBytes;\r
4022         // count colors\r
4023         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4024                 int k = ((int*) pData)[i];\r
4025                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4026                 if(j >= 16) break;\r
4027                 color[j] = k;\r
4028                 if(j >= nrColors) nrColors = j+1;\r
4029         }\r
4030         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4031                 INT p = 0;\r
4032                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4033                     for(w=0; w<(wb>>2); w+=2) {\r
4034                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4035                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4036                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4037                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4038                         pData[p++] = m | j<<4;\r
4039                     }\r
4040                     while(p&3) pData[p++] = 0;\r
4041                 }\r
4042                 fac = 3;\r
4043                 wb = ((wb+31)>>5)<<2;\r
4044         }\r
4045         // write BITMAPFILEHEADER\r
4046         fprintf(diagFile, "BM");\r
4047         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4048         fputDW(diagFile, 0);\r
4049         fputDW(diagFile, 0x36 + (fac?64:0));\r
4050         // write BITMAPINFOHEADER\r
4051         fputDW(diagFile, 40);\r
4052         fputDW(diagFile, b.bmWidth);\r
4053         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4054         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4055         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4056         fputDW(diagFile, 0);\r
4057         fputDW(diagFile, 0);\r
4058         fputDW(diagFile, 0);\r
4059         fputDW(diagFile, 0);\r
4060         fputDW(diagFile, 0);\r
4061         fputDW(diagFile, 0);\r
4062         // write color table\r
4063         if(fac)\r
4064         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4065         // write bitmap data\r
4066         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4067                 fputc(pData[i], diagFile);\r
4068         free(pData);\r
4069      }\r
4070   }\r
4071 \r
4072   SelectObject(tmphdc, oldBitmap);\r
4073 \r
4074   /* Massive cleanup */\r
4075   for (x = 0; x < num_clips; x++)\r
4076     DeleteObject(clips[x]);\r
4077 \r
4078   DeleteDC(tmphdc);\r
4079   DeleteObject(bufferBitmap);\r
4080 \r
4081   if (releaseDC) \r
4082     ReleaseDC(hwndMain, hdc);\r
4083   \r
4084   if (lastDrawnFlipView != flipView && nr == 0) {\r
4085     if (flipView)\r
4086       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4087     else\r
4088       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4089   }\r
4090 \r
4091 /*  CopyBoard(lastDrawn, board);*/\r
4092   lastDrawnHighlight = highlightInfo;\r
4093   lastDrawnPremove   = premoveHighlightInfo;\r
4094   lastDrawnFlipView = flipView;\r
4095   lastDrawnValid[nr] = 1;\r
4096 }\r
4097 \r
4098 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4099 int\r
4100 SaveDiagram(f)\r
4101      FILE *f;\r
4102 {\r
4103     saveDiagFlag = 1; diagFile = f;\r
4104     HDCDrawPosition(NULL, TRUE, NULL);\r
4105     saveDiagFlag = 0;\r
4106 \r
4107     fclose(f);\r
4108     return TRUE;\r
4109 }\r
4110 \r
4111 \r
4112 /*---------------------------------------------------------------------------*\\r
4113 | CLIENT PAINT PROCEDURE\r
4114 |   This is the main event-handler for the WM_PAINT message.\r
4115 |\r
4116 \*---------------------------------------------------------------------------*/\r
4117 VOID\r
4118 PaintProc(HWND hwnd)\r
4119 {\r
4120   HDC         hdc;\r
4121   PAINTSTRUCT ps;\r
4122   HFONT       oldFont;\r
4123 \r
4124   if((hdc = BeginPaint(hwnd, &ps))) {\r
4125     if (IsIconic(hwnd)) {\r
4126       DrawIcon(hdc, 2, 2, iconCurrent);\r
4127     } else {\r
4128       if (!appData.monoMode) {\r
4129         SelectPalette(hdc, hPal, FALSE);\r
4130         RealizePalette(hdc);\r
4131       }\r
4132       HDCDrawPosition(hdc, 1, NULL);\r
4133       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4134         flipView = !flipView; partnerUp = !partnerUp;\r
4135         HDCDrawPosition(hdc, 1, NULL);\r
4136         flipView = !flipView; partnerUp = !partnerUp;\r
4137       }\r
4138       oldFont =\r
4139         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4140       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4141                  ETO_CLIPPED|ETO_OPAQUE,\r
4142                  &messageRect, messageText, strlen(messageText), NULL);\r
4143       SelectObject(hdc, oldFont);\r
4144       DisplayBothClocks();\r
4145       DisplayLogos();\r
4146     }\r
4147     EndPaint(hwnd,&ps);\r
4148   }\r
4149 \r
4150   return;\r
4151 }\r
4152 \r
4153 \r
4154 /*\r
4155  * If the user selects on a border boundary, return -1; if off the board,\r
4156  *   return -2.  Otherwise map the event coordinate to the square.\r
4157  * The offset boardRect.left or boardRect.top must already have been\r
4158  *   subtracted from x.\r
4159  */\r
4160 int EventToSquare(x, limit)\r
4161      int x, limit;\r
4162 {\r
4163   if (x <= border)\r
4164     return -2;\r
4165   if (x < lineGap + border)\r
4166     return -1;\r
4167   x -= lineGap + border;\r
4168   if ((x % (squareSize + lineGap)) >= squareSize)\r
4169     return -1;\r
4170   x /= (squareSize + lineGap);\r
4171     if (x >= limit)\r
4172     return -2;\r
4173   return x;\r
4174 }\r
4175 \r
4176 typedef struct {\r
4177   char piece;\r
4178   int command;\r
4179   char* name;\r
4180 } DropEnable;\r
4181 \r
4182 DropEnable dropEnables[] = {\r
4183   { 'P', DP_Pawn, N_("Pawn") },\r
4184   { 'N', DP_Knight, N_("Knight") },\r
4185   { 'B', DP_Bishop, N_("Bishop") },\r
4186   { 'R', DP_Rook, N_("Rook") },\r
4187   { 'Q', DP_Queen, N_("Queen") },\r
4188 };\r
4189 \r
4190 VOID\r
4191 SetupDropMenu(HMENU hmenu)\r
4192 {\r
4193   int i, count, enable;\r
4194   char *p;\r
4195   extern char white_holding[], black_holding[];\r
4196   char item[MSG_SIZ];\r
4197 \r
4198   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4199     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4200                dropEnables[i].piece);\r
4201     count = 0;\r
4202     while (p && *p++ == dropEnables[i].piece) count++;\r
4203       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4204     enable = count > 0 || !appData.testLegality\r
4205       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4206                       && !appData.icsActive);\r
4207     ModifyMenu(hmenu, dropEnables[i].command,\r
4208                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4209                dropEnables[i].command, item);\r
4210   }\r
4211 }\r
4212 \r
4213 void DragPieceBegin(int x, int y, Boolean instantly)\r
4214 {\r
4215       dragInfo.lastpos.x = boardRect.left + x;\r
4216       dragInfo.lastpos.y = boardRect.top + y;\r
4217       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4218       dragInfo.from.x = fromX;\r
4219       dragInfo.from.y = fromY;\r
4220       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4221       dragInfo.start = dragInfo.from;\r
4222       SetCapture(hwndMain);\r
4223 }\r
4224 \r
4225 void DragPieceEnd(int x, int y)\r
4226 {\r
4227     ReleaseCapture();\r
4228     dragInfo.start.x = dragInfo.start.y = -1;\r
4229     dragInfo.from = dragInfo.start;\r
4230     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4231 }\r
4232 \r
4233 void ChangeDragPiece(ChessSquare piece)\r
4234 {\r
4235     dragInfo.piece = piece;\r
4236 }\r
4237 \r
4238 /* Event handler for mouse messages */\r
4239 VOID\r
4240 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4241 {\r
4242   int x, y, menuNr;\r
4243   POINT pt;\r
4244   static int recursive = 0;\r
4245   HMENU hmenu;\r
4246   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4247 \r
4248   if (recursive) {\r
4249     if (message == WM_MBUTTONUP) {\r
4250       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4251          to the middle button: we simulate pressing the left button too!\r
4252          */\r
4253       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4254       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4255     }\r
4256     return;\r
4257   }\r
4258   recursive++;\r
4259   \r
4260   pt.x = LOWORD(lParam);\r
4261   pt.y = HIWORD(lParam);\r
4262   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4263   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4264   if (!flipView && y >= 0) {\r
4265     y = BOARD_HEIGHT - 1 - y;\r
4266   }\r
4267   if (flipView && x >= 0) {\r
4268     x = BOARD_WIDTH - 1 - x;\r
4269   }\r
4270 \r
4271   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4272   controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status\r
4273 \r
4274   switch (message) {\r
4275   case WM_LBUTTONDOWN:\r
4276       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4277         ClockClick(flipClock); break;\r
4278       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4279         ClockClick(!flipClock); break;\r
4280       }\r
4281     if(dragging) { // [HGM] lion: don't destroy dragging info if we are already dragging\r
4282       dragInfo.start.x = dragInfo.start.y = -1;\r
4283       dragInfo.from = dragInfo.start;\r
4284     }\r
4285     if(fromX == -1 && frozen) { // not sure where this is for\r
4286                 fromX = fromY = -1; \r
4287       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4288       break;\r
4289     }\r
4290       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4291       DrawPosition(TRUE, NULL);\r
4292     break;\r
4293 \r
4294   case WM_LBUTTONUP:\r
4295       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4296       DrawPosition(TRUE, NULL);\r
4297     break;\r
4298 \r
4299   case WM_MOUSEMOVE:\r
4300     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4301     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4302     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4303     if ((appData.animateDragging || appData.highlightDragging)\r
4304         && (wParam & MK_LBUTTON || dragging == 2)\r
4305         && dragInfo.from.x >= 0) \r
4306     {\r
4307       BOOL full_repaint = FALSE;\r
4308 \r
4309       if (appData.animateDragging) {\r
4310         dragInfo.pos = pt;\r
4311       }\r
4312       if (appData.highlightDragging) {\r
4313         HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);\r
4314         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4315             full_repaint = TRUE;\r
4316         }\r
4317       }\r
4318       \r
4319       DrawPosition( full_repaint, NULL);\r
4320       \r
4321       dragInfo.lastpos = dragInfo.pos;\r
4322     }\r
4323     break;\r
4324 \r
4325   case WM_MOUSEWHEEL: // [DM]\r
4326     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4327        /* Mouse Wheel is being rolled forward\r
4328         * Play moves forward\r
4329         */\r
4330        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4331                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4332        /* Mouse Wheel is being rolled backward\r
4333         * Play moves backward\r
4334         */\r
4335        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4336                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4337     }\r
4338     break;\r
4339 \r
4340   case WM_MBUTTONUP:\r
4341   case WM_RBUTTONUP:\r
4342     ReleaseCapture();\r
4343     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4344     break;\r
4345  \r
4346   case WM_MBUTTONDOWN:\r
4347   case WM_RBUTTONDOWN:\r
4348     ErrorPopDown();\r
4349     ReleaseCapture();\r
4350     fromX = fromY = -1;\r
4351     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4352     dragInfo.start.x = dragInfo.start.y = -1;\r
4353     dragInfo.from = dragInfo.start;\r
4354     dragInfo.lastpos = dragInfo.pos;\r
4355     if (appData.highlightDragging) {\r
4356       ClearHighlights();\r
4357     }\r
4358     if(y == -2) {\r
4359       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4360       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4361           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4362       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4363           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4364       }\r
4365       break;\r
4366     }\r
4367     DrawPosition(TRUE, NULL);\r
4368 \r
4369     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4370     switch (menuNr) {\r
4371     case 0:\r
4372       if (message == WM_MBUTTONDOWN) {\r
4373         buttonCount = 3;  /* even if system didn't think so */\r
4374         if (wParam & MK_SHIFT) \r
4375           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4376         else\r
4377           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4378       } else { /* message == WM_RBUTTONDOWN */\r
4379         /* Just have one menu, on the right button.  Windows users don't\r
4380            think to try the middle one, and sometimes other software steals\r
4381            it, or it doesn't really exist. */\r
4382         if(gameInfo.variant != VariantShogi)\r
4383             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4384         else\r
4385             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4386       }\r
4387       break;\r
4388     case 2:\r
4389       SetCapture(hwndMain);\r
4390       break;\r
4391     case 1:\r
4392       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4393       SetupDropMenu(hmenu);\r
4394       MenuPopup(hwnd, pt, hmenu, -1);\r
4395     default:\r
4396       break;\r
4397     }\r
4398     break;\r
4399   }\r
4400 \r
4401   recursive--;\r
4402 }\r
4403 \r
4404 /* Preprocess messages for buttons in main window */\r
4405 LRESULT CALLBACK\r
4406 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4407 {\r
4408   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4409   int i, dir;\r
4410 \r
4411   for (i=0; i<N_BUTTONS; i++) {\r
4412     if (buttonDesc[i].id == id) break;\r
4413   }\r
4414   if (i == N_BUTTONS) return 0;\r
4415   switch (message) {\r
4416   case WM_KEYDOWN:\r
4417     switch (wParam) {\r
4418     case VK_LEFT:\r
4419     case VK_RIGHT:\r
4420       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4421       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4422       return TRUE;\r
4423     }\r
4424     break;\r
4425   case WM_CHAR:\r
4426     switch (wParam) {\r
4427     case '\r':\r
4428       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4429       return TRUE;\r
4430     default:\r
4431       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4432         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4433         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4434         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4435         SetFocus(h);\r
4436         SendMessage(h, WM_CHAR, wParam, lParam);\r
4437         return TRUE;\r
4438       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4439         TypeInEvent((char)wParam);\r
4440       }\r
4441       break;\r
4442     }\r
4443     break;\r
4444   }\r
4445   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4446 }\r
4447 \r
4448 /* Process messages for Promotion dialog box */\r
4449 LRESULT CALLBACK\r
4450 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4451 {\r
4452   char promoChar;\r
4453 \r
4454   switch (message) {\r
4455   case WM_INITDIALOG: /* message: initialize dialog box */\r
4456     /* Center the dialog over the application window */\r
4457     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4458     Translate(hDlg, DLG_PromotionKing);\r
4459     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4460       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4461        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4462        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4463                SW_SHOW : SW_HIDE);\r
4464     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4465     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4466        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4467          PieceToChar(WhiteAngel) != '~') ||\r
4468         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4469          PieceToChar(BlackAngel) != '~')   ) ?\r
4470                SW_SHOW : SW_HIDE);\r
4471     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4472        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4473          PieceToChar(WhiteMarshall) != '~') ||\r
4474         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4475          PieceToChar(BlackMarshall) != '~')   ) ?\r
4476                SW_SHOW : SW_HIDE);\r
4477     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4478     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4479        gameInfo.variant != VariantShogi ?\r
4480                SW_SHOW : SW_HIDE);\r
4481     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4482        gameInfo.variant != VariantShogi ?\r
4483                SW_SHOW : SW_HIDE);\r
4484     if(gameInfo.variant == VariantShogi) {\r
4485         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4486         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4487         SetWindowText(hDlg, "Promote?");\r
4488     }\r
4489     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4490        gameInfo.variant == VariantSuper ?\r
4491                SW_SHOW : SW_HIDE);\r
4492     return TRUE;\r
4493 \r
4494   case WM_COMMAND: /* message: received a command */\r
4495     switch (LOWORD(wParam)) {\r
4496     case IDCANCEL:\r
4497       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4498       ClearHighlights();\r
4499       DrawPosition(FALSE, NULL);\r
4500       return TRUE;\r
4501     case PB_King:\r
4502       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4503       break;\r
4504     case PB_Queen:\r
4505       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4506       break;\r
4507     case PB_Rook:\r
4508       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4509       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4510       break;\r
4511     case PB_Bishop:\r
4512       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4513       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4514       break;\r
4515     case PB_Chancellor:\r
4516       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4517       break;\r
4518     case PB_Archbishop:\r
4519       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4520       break;\r
4521     case PB_Knight:\r
4522       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4523       break;\r
4524     default:\r
4525       return FALSE;\r
4526     }\r
4527     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4528     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4529     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4530     fromX = fromY = -1;\r
4531     if (!appData.highlightLastMove) {\r
4532       ClearHighlights();\r
4533       DrawPosition(FALSE, NULL);\r
4534     }\r
4535     return TRUE;\r
4536   }\r
4537   return FALSE;\r
4538 }\r
4539 \r
4540 /* Pop up promotion dialog */\r
4541 VOID\r
4542 PromotionPopup(HWND hwnd)\r
4543 {\r
4544   FARPROC lpProc;\r
4545 \r
4546   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4547   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4548     hwnd, (DLGPROC)lpProc);\r
4549   FreeProcInstance(lpProc);\r
4550 }\r
4551 \r
4552 void\r
4553 PromotionPopUp()\r
4554 {\r
4555   DrawPosition(TRUE, NULL);\r
4556   PromotionPopup(hwndMain);\r
4557 }\r
4558 \r
4559 VOID\r
4560 LoadGameDialog(HWND hwnd, char* title)\r
4561 {\r
4562   UINT number = 0;\r
4563   FILE *f;\r
4564   char fileTitle[MSG_SIZ];\r
4565   f = OpenFileDialog(hwnd, "rb", "",\r
4566                      appData.oldSaveStyle ? "gam" : "pgn",\r
4567                      GAME_FILT,\r
4568                      title, &number, fileTitle, NULL);\r
4569   if (f != NULL) {\r
4570     cmailMsgLoaded = FALSE;\r
4571     if (number == 0) {\r
4572       int error = GameListBuild(f);\r
4573       if (error) {\r
4574         DisplayError(_("Cannot build game list"), error);\r
4575       } else if (!ListEmpty(&gameList) &&\r
4576                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4577         GameListPopUp(f, fileTitle);\r
4578         return;\r
4579       }\r
4580       GameListDestroy();\r
4581       number = 1;\r
4582     }\r
4583     LoadGame(f, number, fileTitle, FALSE);\r
4584   }\r
4585 }\r
4586 \r
4587 int get_term_width()\r
4588 {\r
4589     HDC hdc;\r
4590     TEXTMETRIC tm;\r
4591     RECT rc;\r
4592     HFONT hfont, hold_font;\r
4593     LOGFONT lf;\r
4594     HWND hText;\r
4595 \r
4596     if (hwndConsole)\r
4597         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4598     else\r
4599         return 79;\r
4600 \r
4601     // get the text metrics\r
4602     hdc = GetDC(hText);\r
4603     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4604     if (consoleCF.dwEffects & CFE_BOLD)\r
4605         lf.lfWeight = FW_BOLD;\r
4606     if (consoleCF.dwEffects & CFE_ITALIC)\r
4607         lf.lfItalic = TRUE;\r
4608     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4609         lf.lfStrikeOut = TRUE;\r
4610     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4611         lf.lfUnderline = TRUE;\r
4612     hfont = CreateFontIndirect(&lf);\r
4613     hold_font = SelectObject(hdc, hfont);\r
4614     GetTextMetrics(hdc, &tm);\r
4615     SelectObject(hdc, hold_font);\r
4616     DeleteObject(hfont);\r
4617     ReleaseDC(hText, hdc);\r
4618 \r
4619     // get the rectangle\r
4620     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4621 \r
4622     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4623 }\r
4624 \r
4625 void UpdateICSWidth(HWND hText)\r
4626 {\r
4627     LONG old_width, new_width;\r
4628 \r
4629     new_width = get_term_width(hText, FALSE);\r
4630     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4631     if (new_width != old_width)\r
4632     {\r
4633         ics_update_width(new_width);\r
4634         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4635     }\r
4636 }\r
4637 \r
4638 VOID\r
4639 ChangedConsoleFont()\r
4640 {\r
4641   CHARFORMAT cfmt;\r
4642   CHARRANGE tmpsel, sel;\r
4643   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4644   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4645   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4646   PARAFORMAT paraf;\r
4647 \r
4648   cfmt.cbSize = sizeof(CHARFORMAT);\r
4649   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4650     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4651                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4652   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4653    * size.  This was undocumented in the version of MSVC++ that I had\r
4654    * when I wrote the code, but is apparently documented now.\r
4655    */\r
4656   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4657   cfmt.bCharSet = f->lf.lfCharSet;\r
4658   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4659   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4660   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4661   /* Why are the following seemingly needed too? */\r
4662   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4663   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4664   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4665   tmpsel.cpMin = 0;\r
4666   tmpsel.cpMax = -1; /*999999?*/\r
4667   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4668   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4669   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4670    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4671    */\r
4672   paraf.cbSize = sizeof(paraf);\r
4673   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4674   paraf.dxStartIndent = 0;\r
4675   paraf.dxOffset = WRAP_INDENT;\r
4676   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4677   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4678   UpdateICSWidth(hText);\r
4679 }\r
4680 \r
4681 /*---------------------------------------------------------------------------*\\r
4682  *\r
4683  * Window Proc for main window\r
4684  *\r
4685 \*---------------------------------------------------------------------------*/\r
4686 \r
4687 /* Process messages for main window, etc. */\r
4688 LRESULT CALLBACK\r
4689 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4690 {\r
4691   FARPROC lpProc;\r
4692   int wmId, wmEvent;\r
4693   char *defName;\r
4694   FILE *f;\r
4695   UINT number;\r
4696   char fileTitle[MSG_SIZ];\r
4697   static SnapData sd;\r
4698   static int peek=0;\r
4699 \r
4700   switch (message) {\r
4701 \r
4702   case WM_PAINT: /* message: repaint portion of window */\r
4703     PaintProc(hwnd);\r
4704     break;\r
4705 \r
4706   case WM_ERASEBKGND:\r
4707     if (IsIconic(hwnd)) {\r
4708       /* Cheat; change the message */\r
4709       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4710     } else {\r
4711       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4712     }\r
4713     break;\r
4714 \r
4715   case WM_LBUTTONDOWN:\r
4716   case WM_MBUTTONDOWN:\r
4717   case WM_RBUTTONDOWN:\r
4718   case WM_LBUTTONUP:\r
4719   case WM_MBUTTONUP:\r
4720   case WM_RBUTTONUP:\r
4721   case WM_MOUSEMOVE:\r
4722   case WM_MOUSEWHEEL:\r
4723     MouseEvent(hwnd, message, wParam, lParam);\r
4724     break;\r
4725 \r
4726   case WM_KEYUP:\r
4727     if((char)wParam == '\b') {\r
4728       ForwardEvent(); peek = 0;\r
4729     }\r
4730 \r
4731     JAWS_KBUP_NAVIGATION\r
4732 \r
4733     break;\r
4734 \r
4735   case WM_KEYDOWN:\r
4736     if((char)wParam == '\b') {\r
4737       if(!peek) BackwardEvent(), peek = 1;\r
4738     }\r
4739 \r
4740     JAWS_KBDOWN_NAVIGATION\r
4741 \r
4742     break;\r
4743 \r
4744   case WM_CHAR:\r
4745     \r
4746     JAWS_ALT_INTERCEPT\r
4747 \r
4748     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4749         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4750         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4751         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4752         SetFocus(h);\r
4753         SendMessage(h, message, wParam, lParam);\r
4754     } else if(lParam != KF_REPEAT) {\r
4755         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4756                 TypeInEvent((char)wParam);\r
4757         } else if((char)wParam == 003) CopyGameToClipboard();\r
4758          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4759     }\r
4760 \r
4761     break;\r
4762 \r
4763   case WM_PALETTECHANGED:\r
4764     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4765       int nnew;\r
4766       HDC hdc = GetDC(hwndMain);\r
4767       SelectPalette(hdc, hPal, TRUE);\r
4768       nnew = RealizePalette(hdc);\r
4769       if (nnew > 0) {\r
4770         paletteChanged = TRUE;\r
4771 \r
4772         InvalidateRect(hwnd, &boardRect, FALSE);\r
4773       }\r
4774       ReleaseDC(hwnd, hdc);\r
4775     }\r
4776     break;\r
4777 \r
4778   case WM_QUERYNEWPALETTE:\r
4779     if (!appData.monoMode /*&& paletteChanged*/) {\r
4780       int nnew;\r
4781       HDC hdc = GetDC(hwndMain);\r
4782       paletteChanged = FALSE;\r
4783       SelectPalette(hdc, hPal, FALSE);\r
4784       nnew = RealizePalette(hdc);\r
4785       if (nnew > 0) {\r
4786         InvalidateRect(hwnd, &boardRect, FALSE);\r
4787       }\r
4788       ReleaseDC(hwnd, hdc);\r
4789       return TRUE;\r
4790     }\r
4791     return FALSE;\r
4792 \r
4793   case WM_COMMAND: /* message: command from application menu */\r
4794     wmId    = LOWORD(wParam);\r
4795     wmEvent = HIWORD(wParam);\r
4796 \r
4797     switch (wmId) {\r
4798     case IDM_NewGame:\r
4799       ResetGameEvent();\r
4800       SAY("new game enter a move to play against the computer with white");\r
4801       break;\r
4802 \r
4803     case IDM_NewGameFRC:\r
4804       if( NewGameFRC() == 0 ) {\r
4805         ResetGameEvent();\r
4806       }\r
4807       break;\r
4808 \r
4809     case IDM_NewVariant:\r
4810       NewVariantPopup(hwnd);\r
4811       break;\r
4812 \r
4813     case IDM_LoadGame:\r
4814       LoadGameDialog(hwnd, _("Load Game from File"));\r
4815       break;\r
4816 \r
4817     case IDM_LoadNextGame:\r
4818       ReloadGame(1);\r
4819       break;\r
4820 \r
4821     case IDM_LoadPrevGame:\r
4822       ReloadGame(-1);\r
4823       break;\r
4824 \r
4825     case IDM_ReloadGame:\r
4826       ReloadGame(0);\r
4827       break;\r
4828 \r
4829     case IDM_LoadPosition:\r
4830       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4831         Reset(FALSE, TRUE);\r
4832       }\r
4833       number = 1;\r
4834       f = OpenFileDialog(hwnd, "rb", "",\r
4835                          appData.oldSaveStyle ? "pos" : "fen",\r
4836                          POSITION_FILT,\r
4837                          _("Load Position from File"), &number, fileTitle, NULL);\r
4838       if (f != NULL) {\r
4839         LoadPosition(f, number, fileTitle);\r
4840       }\r
4841       break;\r
4842 \r
4843     case IDM_LoadNextPosition:\r
4844       ReloadPosition(1);\r
4845       break;\r
4846 \r
4847     case IDM_LoadPrevPosition:\r
4848       ReloadPosition(-1);\r
4849       break;\r
4850 \r
4851     case IDM_ReloadPosition:\r
4852       ReloadPosition(0);\r
4853       break;\r
4854 \r
4855     case IDM_SaveGame:\r
4856       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4857       f = OpenFileDialog(hwnd, "a", defName,\r
4858                          appData.oldSaveStyle ? "gam" : "pgn",\r
4859                          GAME_FILT,\r
4860                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4861       if (f != NULL) {\r
4862         SaveGame(f, 0, "");\r
4863       }\r
4864       break;\r
4865 \r
4866     case IDM_SavePosition:\r
4867       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4868       f = OpenFileDialog(hwnd, "a", defName,\r
4869                          appData.oldSaveStyle ? "pos" : "fen",\r
4870                          POSITION_FILT,\r
4871                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4872       if (f != NULL) {\r
4873         SavePosition(f, 0, "");\r
4874       }\r
4875       break;\r
4876 \r
4877     case IDM_SaveDiagram:\r
4878       defName = "diagram";\r
4879       f = OpenFileDialog(hwnd, "wb", defName,\r
4880                          "bmp",\r
4881                          DIAGRAM_FILT,\r
4882                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4883       if (f != NULL) {\r
4884         SaveDiagram(f);\r
4885       }\r
4886       break;\r
4887 \r
4888     case IDM_CreateBook:\r
4889       CreateBookEvent();\r
4890       break;\r
4891 \r
4892     case IDM_CopyGame:\r
4893       CopyGameToClipboard();\r
4894       break;\r
4895 \r
4896     case IDM_PasteGame:\r
4897       PasteGameFromClipboard();\r
4898       break;\r
4899 \r
4900     case IDM_CopyGameListToClipboard:\r
4901       CopyGameListToClipboard();\r
4902       break;\r
4903 \r
4904     /* [AS] Autodetect FEN or PGN data */\r
4905     case IDM_PasteAny:\r
4906       PasteGameOrFENFromClipboard();\r
4907       break;\r
4908 \r
4909     /* [AS] Move history */\r
4910     case IDM_ShowMoveHistory:\r
4911         if( MoveHistoryIsUp() ) {\r
4912             MoveHistoryPopDown();\r
4913         }\r
4914         else {\r
4915             MoveHistoryPopUp();\r
4916         }\r
4917         break;\r
4918 \r
4919     /* [AS] Eval graph */\r
4920     case IDM_ShowEvalGraph:\r
4921         if( EvalGraphIsUp() ) {\r
4922             EvalGraphPopDown();\r
4923         }\r
4924         else {\r
4925             EvalGraphPopUp();\r
4926             SetFocus(hwndMain);\r
4927         }\r
4928         break;\r
4929 \r
4930     /* [AS] Engine output */\r
4931     case IDM_ShowEngineOutput:\r
4932         if( EngineOutputIsUp() ) {\r
4933             EngineOutputPopDown();\r
4934         }\r
4935         else {\r
4936             EngineOutputPopUp();\r
4937         }\r
4938         break;\r
4939 \r
4940     /* [AS] User adjudication */\r
4941     case IDM_UserAdjudication_White:\r
4942         UserAdjudicationEvent( +1 );\r
4943         break;\r
4944 \r
4945     case IDM_UserAdjudication_Black:\r
4946         UserAdjudicationEvent( -1 );\r
4947         break;\r
4948 \r
4949     case IDM_UserAdjudication_Draw:\r
4950         UserAdjudicationEvent( 0 );\r
4951         break;\r
4952 \r
4953     /* [AS] Game list options dialog */\r
4954     case IDM_GameListOptions:\r
4955       GameListOptions();\r
4956       break;\r
4957 \r
4958     case IDM_NewChat:\r
4959       ChatPopUp(NULL);\r
4960       break;\r
4961 \r
4962     case IDM_CopyPosition:\r
4963       CopyFENToClipboard();\r
4964       break;\r
4965 \r
4966     case IDM_PastePosition:\r
4967       PasteFENFromClipboard();\r
4968       break;\r
4969 \r
4970     case IDM_MailMove:\r
4971       MailMoveEvent();\r
4972       break;\r
4973 \r
4974     case IDM_ReloadCMailMsg:\r
4975       Reset(TRUE, TRUE);\r
4976       ReloadCmailMsgEvent(FALSE);\r
4977       break;\r
4978 \r
4979     case IDM_Minimize:\r
4980       ShowWindow(hwnd, SW_MINIMIZE);\r
4981       break;\r
4982 \r
4983     case IDM_Exit:\r
4984       ExitEvent(0);\r
4985       break;\r
4986 \r
4987     case IDM_MachineWhite:\r
4988       MachineWhiteEvent();\r
4989       /*\r
4990        * refresh the tags dialog only if it's visible\r
4991        */\r
4992       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4993           char *tags;\r
4994           tags = PGNTags(&gameInfo);\r
4995           TagsPopUp(tags, CmailMsg());\r
4996           free(tags);\r
4997       }\r
4998       SAY("computer starts playing white");\r
4999       break;\r
5000 \r
5001     case IDM_MachineBlack:\r
5002       MachineBlackEvent();\r
5003       /*\r
5004        * refresh the tags dialog only if it's visible\r
5005        */\r
5006       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5007           char *tags;\r
5008           tags = PGNTags(&gameInfo);\r
5009           TagsPopUp(tags, CmailMsg());\r
5010           free(tags);\r
5011       }\r
5012       SAY("computer starts playing black");\r
5013       break;\r
5014 \r
5015     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
5016       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
5017       break;\r
5018 \r
5019     case IDM_TwoMachines:\r
5020       TwoMachinesEvent();\r
5021       /*\r
5022        * refresh the tags dialog only if it's visible\r
5023        */\r
5024       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5025           char *tags;\r
5026           tags = PGNTags(&gameInfo);\r
5027           TagsPopUp(tags, CmailMsg());\r
5028           free(tags);\r
5029       }\r
5030       SAY("computer starts playing both sides");\r
5031       break;\r
5032 \r
5033     case IDM_AnalysisMode:\r
5034       if(AnalyzeModeEvent()) {\r
5035         SAY("analyzing current position");\r
5036       }\r
5037       break;\r
5038 \r
5039     case IDM_AnalyzeFile:\r
5040       AnalyzeFileEvent();\r
5041       break;\r
5042 \r
5043     case IDM_IcsClient:\r
5044       IcsClientEvent();\r
5045       break;\r
5046 \r
5047     case IDM_EditGame:\r
5048     case IDM_EditGame2:\r
5049       EditGameEvent();\r
5050       SAY("edit game");\r
5051       break;\r
5052 \r
5053     case IDM_EditPosition:\r
5054     case IDM_EditPosition2:\r
5055       EditPositionEvent();\r
5056       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
5057       break;\r
5058 \r
5059     case IDM_Training:\r
5060       TrainingEvent();\r
5061       break;\r
5062 \r
5063     case IDM_ShowGameList:\r
5064       ShowGameListProc();\r
5065       break;\r
5066 \r
5067     case IDM_EditProgs1:\r
5068       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
5069       break;\r
5070 \r
5071     case IDM_LoadProg1:\r
5072      LoadEnginePopUp(hwndMain, 0);\r
5073       break;\r
5074 \r
5075     case IDM_LoadProg2:\r
5076      LoadEnginePopUp(hwndMain, 1);\r
5077       break;\r
5078 \r
5079     case IDM_EditServers:\r
5080       EditTagsPopUp(icsNames, &icsNames);\r
5081       break;\r
5082 \r
5083     case IDM_EditTags:\r
5084     case IDM_Tags:\r
5085       EditTagsProc();\r
5086       break;\r
5087 \r
5088     case IDM_EditBook:\r
5089       EditBookEvent();\r
5090       break;\r
5091 \r
5092     case IDM_EditComment:\r
5093     case IDM_Comment:\r
5094       if (commentUp && editComment) {\r
5095         CommentPopDown();\r
5096       } else {\r
5097         EditCommentEvent();\r
5098       }\r
5099       break;\r
5100 \r
5101     case IDM_Pause:\r
5102       PauseEvent();\r
5103       break;\r
5104 \r
5105     case IDM_Accept:\r
5106       AcceptEvent();\r
5107       break;\r
5108 \r
5109     case IDM_Decline:\r
5110       DeclineEvent();\r
5111       break;\r
5112 \r
5113     case IDM_Rematch:\r
5114 \r
5115       RematchEvent();\r
5116       break;\r
5117 \r
5118     case IDM_CallFlag:\r
5119       CallFlagEvent();\r
5120       break;\r
5121 \r
5122     case IDM_Draw:\r
5123       DrawEvent();\r
5124       break;\r
5125 \r
5126     case IDM_Adjourn:\r
5127       AdjournEvent();\r
5128       break;\r
5129 \r
5130     case IDM_Abort:\r
5131       AbortEvent();\r
5132       break;\r
5133 \r
5134     case IDM_Resign:\r
5135       ResignEvent();\r
5136       break;\r
5137 \r
5138     case IDM_StopObserving:\r
5139       StopObservingEvent();\r
5140       break;\r
5141 \r
5142     case IDM_StopExamining:\r
5143       StopExaminingEvent();\r
5144       break;\r
5145 \r
5146     case IDM_Upload:\r
5147       UploadGameEvent();\r
5148       break;\r
5149 \r
5150     case IDM_TypeInMove:\r
5151       TypeInEvent('\000');\r
5152       break;\r
5153 \r
5154     case IDM_TypeInName:\r
5155       PopUpNameDialog('\000');\r
5156       break;\r
5157 \r
5158     case IDM_Backward:\r
5159       BackwardEvent();\r
5160       SetFocus(hwndMain);\r
5161       break;\r
5162 \r
5163     JAWS_MENU_ITEMS\r
5164 \r
5165     case IDM_Forward:\r
5166       ForwardEvent();\r
5167       SetFocus(hwndMain);\r
5168       break;\r
5169 \r
5170     case IDM_ToStart:\r
5171       ToStartEvent();\r
5172       SetFocus(hwndMain);\r
5173       break;\r
5174 \r
5175     case IDM_ToEnd:\r
5176       ToEndEvent();\r
5177       SetFocus(hwndMain);\r
5178       break;\r
5179 \r
5180     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5181     case OPT_GameListPrev:\r
5182       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5183       break;\r
5184 \r
5185     case IDM_Revert:\r
5186       RevertEvent(FALSE);\r
5187       break;\r
5188 \r
5189     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5190       RevertEvent(TRUE);\r
5191       break;\r
5192 \r
5193     case IDM_TruncateGame:\r
5194       TruncateGameEvent();\r
5195       break;\r
5196 \r
5197     case IDM_MoveNow:\r
5198       MoveNowEvent();\r
5199       break;\r
5200 \r
5201     case IDM_RetractMove:\r
5202       RetractMoveEvent();\r
5203       break;\r
5204 \r
5205     case IDM_FlipView:\r
5206       flipView = !flipView;\r
5207       DrawPosition(FALSE, NULL);\r
5208       break;\r
5209 \r
5210     case IDM_FlipClock:\r
5211       flipClock = !flipClock;\r
5212       DisplayBothClocks();\r
5213       DisplayLogos();\r
5214       break;\r
5215 \r
5216     case IDM_MuteSounds:\r
5217       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5218       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5219                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5220       break;\r
5221 \r
5222     case IDM_GeneralOptions:\r
5223       GeneralOptionsPopup(hwnd);\r
5224       DrawPosition(TRUE, NULL);\r
5225       break;\r
5226 \r
5227     case IDM_BoardOptions:\r
5228       BoardOptionsPopup(hwnd);\r
5229       break;\r
5230 \r
5231     case IDM_ThemeOptions:\r
5232       ThemeOptionsPopup(hwnd);\r
5233       break;\r
5234 \r
5235     case IDM_EnginePlayOptions:\r
5236       EnginePlayOptionsPopup(hwnd);\r
5237       break;\r
5238 \r
5239     case IDM_Engine1Options:\r
5240       EngineOptionsPopup(hwnd, &first);\r
5241       break;\r
5242 \r
5243     case IDM_Engine2Options:\r
5244       savedHwnd = hwnd;\r
5245       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5246       EngineOptionsPopup(hwnd, &second);\r
5247       break;\r
5248 \r
5249     case IDM_OptionsUCI:\r
5250       UciOptionsPopup(hwnd);\r
5251       break;\r
5252 \r
5253     case IDM_Tourney:\r
5254       TourneyPopup(hwnd);\r
5255       break;\r
5256 \r
5257     case IDM_IcsOptions:\r
5258       IcsOptionsPopup(hwnd);\r
5259       break;\r
5260 \r
5261     case IDM_Fonts:\r
5262       FontsOptionsPopup(hwnd);\r
5263       break;\r
5264 \r
5265     case IDM_Sounds:\r
5266       SoundOptionsPopup(hwnd);\r
5267       break;\r
5268 \r
5269     case IDM_CommPort:\r
5270       CommPortOptionsPopup(hwnd);\r
5271       break;\r
5272 \r
5273     case IDM_LoadOptions:\r
5274       LoadOptionsPopup(hwnd);\r
5275       break;\r
5276 \r
5277     case IDM_SaveOptions:\r
5278       SaveOptionsPopup(hwnd);\r
5279       break;\r
5280 \r
5281     case IDM_TimeControl:\r
5282       TimeControlOptionsPopup(hwnd);\r
5283       break;\r
5284 \r
5285     case IDM_SaveSettings:\r
5286       SaveSettings(settingsFileName);\r
5287       break;\r
5288 \r
5289     case IDM_SaveSettingsOnExit:\r
5290       saveSettingsOnExit = !saveSettingsOnExit;\r
5291       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5292                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5293                                          MF_CHECKED : MF_UNCHECKED));\r
5294       break;\r
5295 \r
5296     case IDM_Hint:\r
5297       HintEvent();\r
5298       break;\r
5299 \r
5300     case IDM_Book:\r
5301       BookEvent();\r
5302       break;\r
5303 \r
5304     case IDM_AboutGame:\r
5305       AboutGameEvent();\r
5306       break;\r
5307 \r
5308     case IDM_Debug:\r
5309       appData.debugMode = !appData.debugMode;\r
5310       if (appData.debugMode) {\r
5311         char dir[MSG_SIZ];\r
5312         GetCurrentDirectory(MSG_SIZ, dir);\r
5313         SetCurrentDirectory(installDir);\r
5314         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5315         SetCurrentDirectory(dir);\r
5316         setbuf(debugFP, NULL);\r
5317       } else {\r
5318         fclose(debugFP);\r
5319         debugFP = NULL;\r
5320       }\r
5321       break;\r
5322 \r
5323     case IDM_HELPCONTENTS:\r
5324       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5325           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5326           MessageBox (GetFocus(),\r
5327                     _("Unable to activate help"),\r
5328                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5329       }\r
5330       break;\r
5331 \r
5332     case IDM_HELPSEARCH:\r
5333         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5334             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5335         MessageBox (GetFocus(),\r
5336                     _("Unable to activate help"),\r
5337                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5338       }\r
5339       break;\r
5340 \r
5341     case IDM_HELPHELP:\r
5342       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5343         MessageBox (GetFocus(),\r
5344                     _("Unable to activate help"),\r
5345                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5346       }\r
5347       break;\r
5348 \r
5349     case IDM_ABOUT:\r
5350       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5351       DialogBox(hInst, \r
5352         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5353         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5354       FreeProcInstance(lpProc);\r
5355       break;\r
5356 \r
5357     case IDM_DirectCommand1:\r
5358       AskQuestionEvent(_("Direct Command"),\r
5359                        _("Send to chess program:"), "", "1");\r
5360       break;\r
5361     case IDM_DirectCommand2:\r
5362       AskQuestionEvent(_("Direct Command"),\r
5363                        _("Send to second chess program:"), "", "2");\r
5364       break;\r
5365 \r
5366     case EP_WhitePawn:\r
5367       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5368       fromX = fromY = -1;\r
5369       break;\r
5370 \r
5371     case EP_WhiteKnight:\r
5372       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5373       fromX = fromY = -1;\r
5374       break;\r
5375 \r
5376     case EP_WhiteBishop:\r
5377       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5378       fromX = fromY = -1;\r
5379       break;\r
5380 \r
5381     case EP_WhiteRook:\r
5382       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5383       fromX = fromY = -1;\r
5384       break;\r
5385 \r
5386     case EP_WhiteQueen:\r
5387       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5388       fromX = fromY = -1;\r
5389       break;\r
5390 \r
5391     case EP_WhiteFerz:\r
5392       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5393       fromX = fromY = -1;\r
5394       break;\r
5395 \r
5396     case EP_WhiteWazir:\r
5397       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5398       fromX = fromY = -1;\r
5399       break;\r
5400 \r
5401     case EP_WhiteAlfil:\r
5402       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5403       fromX = fromY = -1;\r
5404       break;\r
5405 \r
5406     case EP_WhiteCannon:\r
5407       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5408       fromX = fromY = -1;\r
5409       break;\r
5410 \r
5411     case EP_WhiteCardinal:\r
5412       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5413       fromX = fromY = -1;\r
5414       break;\r
5415 \r
5416     case EP_WhiteMarshall:\r
5417       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5418       fromX = fromY = -1;\r
5419       break;\r
5420 \r
5421     case EP_WhiteKing:\r
5422       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5423       fromX = fromY = -1;\r
5424       break;\r
5425 \r
5426     case EP_BlackPawn:\r
5427       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5428       fromX = fromY = -1;\r
5429       break;\r
5430 \r
5431     case EP_BlackKnight:\r
5432       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5433       fromX = fromY = -1;\r
5434       break;\r
5435 \r
5436     case EP_BlackBishop:\r
5437       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5438       fromX = fromY = -1;\r
5439       break;\r
5440 \r
5441     case EP_BlackRook:\r
5442       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5443       fromX = fromY = -1;\r
5444       break;\r
5445 \r
5446     case EP_BlackQueen:\r
5447       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5448       fromX = fromY = -1;\r
5449       break;\r
5450 \r
5451     case EP_BlackFerz:\r
5452       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5453       fromX = fromY = -1;\r
5454       break;\r
5455 \r
5456     case EP_BlackWazir:\r
5457       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5458       fromX = fromY = -1;\r
5459       break;\r
5460 \r
5461     case EP_BlackAlfil:\r
5462       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5463       fromX = fromY = -1;\r
5464       break;\r
5465 \r
5466     case EP_BlackCannon:\r
5467       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5468       fromX = fromY = -1;\r
5469       break;\r
5470 \r
5471     case EP_BlackCardinal:\r
5472       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5473       fromX = fromY = -1;\r
5474       break;\r
5475 \r
5476     case EP_BlackMarshall:\r
5477       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5478       fromX = fromY = -1;\r
5479       break;\r
5480 \r
5481     case EP_BlackKing:\r
5482       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5483       fromX = fromY = -1;\r
5484       break;\r
5485 \r
5486     case EP_EmptySquare:\r
5487       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5488       fromX = fromY = -1;\r
5489       break;\r
5490 \r
5491     case EP_ClearBoard:\r
5492       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5493       fromX = fromY = -1;\r
5494       break;\r
5495 \r
5496     case EP_White:\r
5497       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5498       fromX = fromY = -1;\r
5499       break;\r
5500 \r
5501     case EP_Black:\r
5502       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5503       fromX = fromY = -1;\r
5504       break;\r
5505 \r
5506     case EP_Promote:\r
5507       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5508       fromX = fromY = -1;\r
5509       break;\r
5510 \r
5511     case EP_Demote:\r
5512       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5513       fromX = fromY = -1;\r
5514       break;\r
5515 \r
5516     case DP_Pawn:\r
5517       DropMenuEvent(WhitePawn, fromX, fromY);\r
5518       fromX = fromY = -1;\r
5519       break;\r
5520 \r
5521     case DP_Knight:\r
5522       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5523       fromX = fromY = -1;\r
5524       break;\r
5525 \r
5526     case DP_Bishop:\r
5527       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5528       fromX = fromY = -1;\r
5529       break;\r
5530 \r
5531     case DP_Rook:\r
5532       DropMenuEvent(WhiteRook, fromX, fromY);\r
5533       fromX = fromY = -1;\r
5534       break;\r
5535 \r
5536     case DP_Queen:\r
5537       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5538       fromX = fromY = -1;\r
5539       break;\r
5540 \r
5541     case IDM_English:\r
5542       barbaric = 0; appData.language = "";\r
5543       TranslateMenus(0);\r
5544       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5545       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5546       lastChecked = wmId;\r
5547       break;\r
5548 \r
5549     default:\r
5550       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5551           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5552       else\r
5553       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5554           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5555           TranslateMenus(0);\r
5556           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5557           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5558           lastChecked = wmId;\r
5559           break;\r
5560       }\r
5561       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5562     }\r
5563     break;\r
5564 \r
5565   case WM_TIMER:\r
5566     switch (wParam) {\r
5567     case CLOCK_TIMER_ID:\r
5568       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5569       clockTimerEvent = 0;\r
5570       DecrementClocks(); /* call into back end */\r
5571       break;\r
5572     case LOAD_GAME_TIMER_ID:\r
5573       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5574       loadGameTimerEvent = 0;\r
5575       AutoPlayGameLoop(); /* call into back end */\r
5576       break;\r
5577     case ANALYSIS_TIMER_ID:\r
5578       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5579                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5580         AnalysisPeriodicEvent(0);\r
5581       } else {\r
5582         KillTimer(hwnd, analysisTimerEvent);\r
5583         analysisTimerEvent = 0;\r
5584       }\r
5585       break;\r
5586     case DELAYED_TIMER_ID:\r
5587       KillTimer(hwnd, delayedTimerEvent);\r
5588       delayedTimerEvent = 0;\r
5589       delayedTimerCallback();\r
5590       break;\r
5591     }\r
5592     break;\r
5593 \r
5594   case WM_USER_Input:\r
5595     InputEvent(hwnd, message, wParam, lParam);\r
5596     break;\r
5597 \r
5598   /* [AS] Also move "attached" child windows */\r
5599   case WM_WINDOWPOSCHANGING:\r
5600 \r
5601     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5602         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5603 \r
5604         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5605             /* Window is moving */\r
5606             RECT rcMain;\r
5607 \r
5608 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5609             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5610             rcMain.right  = wpMain.x + wpMain.width;\r
5611             rcMain.top    = wpMain.y;\r
5612             rcMain.bottom = wpMain.y + wpMain.height;\r
5613             \r
5614             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5615             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5616             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5617             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5618             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5619             wpMain.x = lpwp->x;\r
5620             wpMain.y = lpwp->y;\r
5621         }\r
5622     }\r
5623     break;\r
5624 \r
5625   /* [AS] Snapping */\r
5626   case WM_ENTERSIZEMOVE:\r
5627     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5628     if (hwnd == hwndMain) {\r
5629       doingSizing = TRUE;\r
5630       lastSizing = 0;\r
5631     }\r
5632     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5633     break;\r
5634 \r
5635   case WM_SIZING:\r
5636     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5637     if (hwnd == hwndMain) {\r
5638       lastSizing = wParam;\r
5639     }\r
5640     break;\r
5641 \r
5642   case WM_MOVING:\r
5643     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5644       return OnMoving( &sd, hwnd, wParam, lParam );\r
5645 \r
5646   case WM_EXITSIZEMOVE:\r
5647     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5648     if (hwnd == hwndMain) {\r
5649       RECT client;\r
5650       doingSizing = FALSE;\r
5651       InvalidateRect(hwnd, &boardRect, FALSE);\r
5652       GetClientRect(hwnd, &client);\r
5653       ResizeBoard(client.right, client.bottom, lastSizing);\r
5654       lastSizing = 0;\r
5655       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5656     }\r
5657     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5658     break;\r
5659 \r
5660   case WM_DESTROY: /* message: window being destroyed */\r
5661     PostQuitMessage(0);\r
5662     break;\r
5663 \r
5664   case WM_CLOSE:\r
5665     if (hwnd == hwndMain) {\r
5666       ExitEvent(0);\r
5667     }\r
5668     break;\r
5669 \r
5670   default:      /* Passes it on if unprocessed */\r
5671     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5672   }\r
5673   return 0;\r
5674 }\r
5675 \r
5676 /*---------------------------------------------------------------------------*\\r
5677  *\r
5678  * Misc utility routines\r
5679  *\r
5680 \*---------------------------------------------------------------------------*/\r
5681 \r
5682 /*\r
5683  * Decent random number generator, at least not as bad as Windows\r
5684  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5685  */\r
5686 unsigned int randstate;\r
5687 \r
5688 int\r
5689 myrandom(void)\r
5690 {\r
5691   randstate = randstate * 1664525 + 1013904223;\r
5692   return (int) randstate & 0x7fffffff;\r
5693 }\r
5694 \r
5695 void\r
5696 mysrandom(unsigned int seed)\r
5697 {\r
5698   randstate = seed;\r
5699 }\r
5700 \r
5701 \r
5702 /* \r
5703  * returns TRUE if user selects a different color, FALSE otherwise \r
5704  */\r
5705 \r
5706 BOOL\r
5707 ChangeColor(HWND hwnd, COLORREF *which)\r
5708 {\r
5709   static BOOL firstTime = TRUE;\r
5710   static DWORD customColors[16];\r
5711   CHOOSECOLOR cc;\r
5712   COLORREF newcolor;\r
5713   int i;\r
5714   ColorClass ccl;\r
5715 \r
5716   if (firstTime) {\r
5717     /* Make initial colors in use available as custom colors */\r
5718     /* Should we put the compiled-in defaults here instead? */\r
5719     i = 0;\r
5720     customColors[i++] = lightSquareColor & 0xffffff;\r
5721     customColors[i++] = darkSquareColor & 0xffffff;\r
5722     customColors[i++] = whitePieceColor & 0xffffff;\r
5723     customColors[i++] = blackPieceColor & 0xffffff;\r
5724     customColors[i++] = highlightSquareColor & 0xffffff;\r
5725     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5726 \r
5727     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5728       customColors[i++] = textAttribs[ccl].color;\r
5729     }\r
5730     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5731     firstTime = FALSE;\r
5732   }\r
5733 \r
5734   cc.lStructSize = sizeof(cc);\r
5735   cc.hwndOwner = hwnd;\r
5736   cc.hInstance = NULL;\r
5737   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5738   cc.lpCustColors = (LPDWORD) customColors;\r
5739   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5740 \r
5741   if (!ChooseColor(&cc)) return FALSE;\r
5742 \r
5743   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5744   if (newcolor == *which) return FALSE;\r
5745   *which = newcolor;\r
5746   return TRUE;\r
5747 \r
5748   /*\r
5749   InitDrawingColors();\r
5750   InvalidateRect(hwnd, &boardRect, FALSE);\r
5751   */\r
5752 }\r
5753 \r
5754 BOOLEAN\r
5755 MyLoadSound(MySound *ms)\r
5756 {\r
5757   BOOL ok = FALSE;\r
5758   struct stat st;\r
5759   FILE *f;\r
5760 \r
5761   if (ms->data && ms->flag) free(ms->data);\r
5762   ms->data = NULL;\r
5763 \r
5764   switch (ms->name[0]) {\r
5765   case NULLCHAR:\r
5766     /* Silence */\r
5767     ok = TRUE;\r
5768     break;\r
5769   case '$':\r
5770     /* System sound from Control Panel.  Don't preload here. */\r
5771     ok = TRUE;\r
5772     break;\r
5773   case '!':\r
5774     if (ms->name[1] == NULLCHAR) {\r
5775       /* "!" alone = silence */\r
5776       ok = TRUE;\r
5777     } else {\r
5778       /* Builtin wave resource.  Error if not found. */\r
5779       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5780       if (h == NULL) break;\r
5781       ms->data = (void *)LoadResource(hInst, h);\r
5782       ms->flag = 0; // not maloced, so cannot be freed!\r
5783       if (h == NULL) break;\r
5784       ok = TRUE;\r
5785     }\r
5786     break;\r
5787   default:\r
5788     /* .wav file.  Error if not found. */\r
5789     f = fopen(ms->name, "rb");\r
5790     if (f == NULL) break;\r
5791     if (fstat(fileno(f), &st) < 0) break;\r
5792     ms->data = malloc(st.st_size);\r
5793     ms->flag = 1;\r
5794     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5795     fclose(f);\r
5796     ok = TRUE;\r
5797     break;\r
5798   }\r
5799   if (!ok) {\r
5800     char buf[MSG_SIZ];\r
5801       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5802     DisplayError(buf, GetLastError());\r
5803   }\r
5804   return ok;\r
5805 }\r
5806 \r
5807 BOOLEAN\r
5808 MyPlaySound(MySound *ms)\r
5809 {\r
5810   BOOLEAN ok = FALSE;\r
5811 \r
5812   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5813   switch (ms->name[0]) {\r
5814   case NULLCHAR:\r
5815         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5816     /* Silence */\r
5817     ok = TRUE;\r
5818     break;\r
5819   case '$':\r
5820     /* System sound from Control Panel (deprecated feature).\r
5821        "$" alone or an unset sound name gets default beep (still in use). */\r
5822     if (ms->name[1]) {\r
5823       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5824     }\r
5825     if (!ok) ok = MessageBeep(MB_OK);\r
5826     break; \r
5827   case '!':\r
5828     /* Builtin wave resource, or "!" alone for silence */\r
5829     if (ms->name[1]) {\r
5830       if (ms->data == NULL) return FALSE;\r
5831       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5832     } else {\r
5833       ok = TRUE;\r
5834     }\r
5835     break;\r
5836   default:\r
5837     /* .wav file.  Error if not found. */\r
5838     if (ms->data == NULL) return FALSE;\r
5839     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5840     break;\r
5841   }\r
5842   /* Don't print an error: this can happen innocently if the sound driver\r
5843      is busy; for instance, if another instance of WinBoard is playing\r
5844      a sound at about the same time. */\r
5845   return ok;\r
5846 }\r
5847 \r
5848 \r
5849 LRESULT CALLBACK\r
5850 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5851 {\r
5852   BOOL ok;\r
5853   OPENFILENAME *ofn;\r
5854   static UINT *number; /* gross that this is static */\r
5855 \r
5856   switch (message) {\r
5857   case WM_INITDIALOG: /* message: initialize dialog box */\r
5858     /* Center the dialog over the application window */\r
5859     ofn = (OPENFILENAME *) lParam;\r
5860     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5861       number = (UINT *) ofn->lCustData;\r
5862       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5863     } else {\r
5864       number = NULL;\r
5865     }\r
5866     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5867     Translate(hDlg, 1536);\r
5868     return FALSE;  /* Allow for further processing */\r
5869 \r
5870   case WM_COMMAND:\r
5871     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5872       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5873     }\r
5874     return FALSE;  /* Allow for further processing */\r
5875   }\r
5876   return FALSE;\r
5877 }\r
5878 \r
5879 UINT APIENTRY\r
5880 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5881 {\r
5882   static UINT *number;\r
5883   OPENFILENAME *ofname;\r
5884   OFNOTIFY *ofnot;\r
5885   switch (uiMsg) {\r
5886   case WM_INITDIALOG:\r
5887     Translate(hdlg, DLG_IndexNumber);\r
5888     ofname = (OPENFILENAME *)lParam;\r
5889     number = (UINT *)(ofname->lCustData);\r
5890     break;\r
5891   case WM_NOTIFY:\r
5892     ofnot = (OFNOTIFY *)lParam;\r
5893     if (ofnot->hdr.code == CDN_FILEOK) {\r
5894       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5895     }\r
5896     break;\r
5897   }\r
5898   return 0;\r
5899 }\r
5900 \r
5901 \r
5902 FILE *\r
5903 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5904                char *nameFilt, char *dlgTitle, UINT *number,\r
5905                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5906 {\r
5907   OPENFILENAME openFileName;\r
5908   char buf1[MSG_SIZ];\r
5909   FILE *f;\r
5910 \r
5911   if (fileName == NULL) fileName = buf1;\r
5912   if (defName == NULL) {\r
5913     safeStrCpy(fileName, "*.", 3 );\r
5914     strcat(fileName, defExt);\r
5915   } else {\r
5916     safeStrCpy(fileName, defName, MSG_SIZ );\r
5917   }\r
5918     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5919   if (number) *number = 0;\r
5920 \r
5921   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5922   openFileName.hwndOwner         = hwnd;\r
5923   openFileName.hInstance         = (HANDLE) hInst;\r
5924   openFileName.lpstrFilter       = nameFilt;\r
5925   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5926   openFileName.nMaxCustFilter    = 0L;\r
5927   openFileName.nFilterIndex      = 1L;\r
5928   openFileName.lpstrFile         = fileName;\r
5929   openFileName.nMaxFile          = MSG_SIZ;\r
5930   openFileName.lpstrFileTitle    = fileTitle;\r
5931   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5932   openFileName.lpstrInitialDir   = NULL;\r
5933   openFileName.lpstrTitle        = dlgTitle;\r
5934   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5935     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5936     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5937     | (oldDialog ? 0 : OFN_EXPLORER);\r
5938   openFileName.nFileOffset       = 0;\r
5939   openFileName.nFileExtension    = 0;\r
5940   openFileName.lpstrDefExt       = defExt;\r
5941   openFileName.lCustData         = (LONG) number;\r
5942   openFileName.lpfnHook          = oldDialog ?\r
5943     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5944   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5945 \r
5946   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5947                         GetOpenFileName(&openFileName)) {\r
5948     /* open the file */\r
5949     f = fopen(openFileName.lpstrFile, write);\r
5950     if (f == NULL) {\r
5951       MessageBox(hwnd, _("File open failed"), NULL,\r
5952                  MB_OK|MB_ICONEXCLAMATION);\r
5953       return NULL;\r
5954     }\r
5955   } else {\r
5956     int err = CommDlgExtendedError();\r
5957     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5958     return FALSE;\r
5959   }\r
5960   return f;\r
5961 }\r
5962 \r
5963 \r
5964 \r
5965 VOID APIENTRY\r
5966 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5967 {\r
5968   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5969 \r
5970   /*\r
5971    * Get the first pop-up menu in the menu template. This is the\r
5972    * menu that TrackPopupMenu displays.\r
5973    */\r
5974   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5975   TranslateOneMenu(10, hmenuTrackPopup);\r
5976 \r
5977   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5978 \r
5979   /*\r
5980    * TrackPopup uses screen coordinates, so convert the\r
5981    * coordinates of the mouse click to screen coordinates.\r
5982    */\r
5983   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5984 \r
5985   /* Draw and track the floating pop-up menu. */\r
5986   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5987                  pt.x, pt.y, 0, hwnd, NULL);\r
5988 \r
5989   /* Destroy the menu.*/\r
5990   DestroyMenu(hmenu);\r
5991 }\r
5992    \r
5993 typedef struct {\r
5994   HWND hDlg, hText;\r
5995   int sizeX, sizeY, newSizeX, newSizeY;\r
5996   HDWP hdwp;\r
5997 } ResizeEditPlusButtonsClosure;\r
5998 \r
5999 BOOL CALLBACK\r
6000 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6001 {\r
6002   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6003   RECT rect;\r
6004   POINT pt;\r
6005 \r
6006   if (hChild == cl->hText) return TRUE;\r
6007   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6008   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6009   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6010   ScreenToClient(cl->hDlg, &pt);\r
6011   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6012     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6013   return TRUE;\r
6014 }\r
6015 \r
6016 /* Resize a dialog that has a (rich) edit field filling most of\r
6017    the top, with a row of buttons below */\r
6018 VOID\r
6019 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6020 {\r
6021   RECT rectText;\r
6022   int newTextHeight, newTextWidth;\r
6023   ResizeEditPlusButtonsClosure cl;\r
6024   \r
6025   /*if (IsIconic(hDlg)) return;*/\r
6026   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6027   \r
6028   cl.hdwp = BeginDeferWindowPos(8);\r
6029 \r
6030   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6031   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6032   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6033   if (newTextHeight < 0) {\r
6034     newSizeY += -newTextHeight;\r
6035     newTextHeight = 0;\r
6036   }\r
6037   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6038     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6039 \r
6040   cl.hDlg = hDlg;\r
6041   cl.hText = hText;\r
6042   cl.sizeX = sizeX;\r
6043   cl.sizeY = sizeY;\r
6044   cl.newSizeX = newSizeX;\r
6045   cl.newSizeY = newSizeY;\r
6046   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6047 \r
6048   EndDeferWindowPos(cl.hdwp);\r
6049 }\r
6050 \r
6051 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6052 {\r
6053     RECT    rChild, rParent;\r
6054     int     wChild, hChild, wParent, hParent;\r
6055     int     wScreen, hScreen, xNew, yNew;\r
6056     HDC     hdc;\r
6057 \r
6058     /* Get the Height and Width of the child window */\r
6059     GetWindowRect (hwndChild, &rChild);\r
6060     wChild = rChild.right - rChild.left;\r
6061     hChild = rChild.bottom - rChild.top;\r
6062 \r
6063     /* Get the Height and Width of the parent window */\r
6064     GetWindowRect (hwndParent, &rParent);\r
6065     wParent = rParent.right - rParent.left;\r
6066     hParent = rParent.bottom - rParent.top;\r
6067 \r
6068     /* Get the display limits */\r
6069     hdc = GetDC (hwndChild);\r
6070     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6071     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6072     ReleaseDC(hwndChild, hdc);\r
6073 \r
6074     /* Calculate new X position, then adjust for screen */\r
6075     xNew = rParent.left + ((wParent - wChild) /2);\r
6076     if (xNew < 0) {\r
6077         xNew = 0;\r
6078     } else if ((xNew+wChild) > wScreen) {\r
6079         xNew = wScreen - wChild;\r
6080     }\r
6081 \r
6082     /* Calculate new Y position, then adjust for screen */\r
6083     if( mode == 0 ) {\r
6084         yNew = rParent.top  + ((hParent - hChild) /2);\r
6085     }\r
6086     else {\r
6087         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6088     }\r
6089 \r
6090     if (yNew < 0) {\r
6091         yNew = 0;\r
6092     } else if ((yNew+hChild) > hScreen) {\r
6093         yNew = hScreen - hChild;\r
6094     }\r
6095 \r
6096     /* Set it, and return */\r
6097     return SetWindowPos (hwndChild, NULL,\r
6098                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6099 }\r
6100 \r
6101 /* Center one window over another */\r
6102 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6103 {\r
6104     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6105 }\r
6106 \r
6107 /*---------------------------------------------------------------------------*\\r
6108  *\r
6109  * Startup Dialog functions\r
6110  *\r
6111 \*---------------------------------------------------------------------------*/\r
6112 void\r
6113 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6114 {\r
6115   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6116 \r
6117   while (*cd != NULL) {\r
6118     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6119     cd++;\r
6120   }\r
6121 }\r
6122 \r
6123 void\r
6124 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6125 {\r
6126   char buf1[MAX_ARG_LEN];\r
6127   int len;\r
6128 \r
6129   if (str[0] == '@') {\r
6130     FILE* f = fopen(str + 1, "r");\r
6131     if (f == NULL) {\r
6132       DisplayFatalError(str + 1, errno, 2);\r
6133       return;\r
6134     }\r
6135     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6136     fclose(f);\r
6137     buf1[len] = NULLCHAR;\r
6138     str = buf1;\r
6139   }\r
6140 \r
6141   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6142 \r
6143   for (;;) {\r
6144     char buf[MSG_SIZ];\r
6145     char *end = strchr(str, '\n');\r
6146     if (end == NULL) return;\r
6147     memcpy(buf, str, end - str);\r
6148     buf[end - str] = NULLCHAR;\r
6149     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6150     str = end + 1;\r
6151   }\r
6152 }\r
6153 \r
6154 void\r
6155 SetStartupDialogEnables(HWND hDlg)\r
6156 {\r
6157   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6158     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6159     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6160   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6161     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6162   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6163     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6164   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6165     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6166   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6167     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6168     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6169     IsDlgButtonChecked(hDlg, OPT_View));\r
6170 }\r
6171 \r
6172 char *\r
6173 QuoteForFilename(char *filename)\r
6174 {\r
6175   int dquote, space;\r
6176   dquote = strchr(filename, '"') != NULL;\r
6177   space = strchr(filename, ' ') != NULL;\r
6178   if (dquote || space) {\r
6179     if (dquote) {\r
6180       return "'";\r
6181     } else {\r
6182       return "\"";\r
6183     }\r
6184   } else {\r
6185     return "";\r
6186   }\r
6187 }\r
6188 \r
6189 VOID\r
6190 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6191 {\r
6192   char buf[MSG_SIZ];\r
6193   char *q;\r
6194 \r
6195   InitComboStringsFromOption(hwndCombo, nthnames);\r
6196   q = QuoteForFilename(nthcp);\r
6197     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6198   if (*nthdir != NULLCHAR) {\r
6199     q = QuoteForFilename(nthdir);\r
6200       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6201   }\r
6202   if (*nthcp == NULLCHAR) {\r
6203     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6204   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6205     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6206     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6207   }\r
6208 }\r
6209 \r
6210 LRESULT CALLBACK\r
6211 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6212 {\r
6213   char buf[MSG_SIZ];\r
6214   HANDLE hwndCombo;\r
6215   char *p;\r
6216 \r
6217   switch (message) {\r
6218   case WM_INITDIALOG:\r
6219     /* Center the dialog */\r
6220     CenterWindow (hDlg, GetDesktopWindow());\r
6221     Translate(hDlg, DLG_Startup);\r
6222     /* Initialize the dialog items */\r
6223     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6224                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6225                   firstChessProgramNames);\r
6226     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6227                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6228                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6229     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6230     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6231       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6232     if (*appData.icsHelper != NULLCHAR) {\r
6233       char *q = QuoteForFilename(appData.icsHelper);\r
6234       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6235     }\r
6236     if (*appData.icsHost == NULLCHAR) {\r
6237       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6238       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6239     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6240       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6241       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6242     }\r
6243 \r
6244     if (appData.icsActive) {\r
6245       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6246     }\r
6247     else if (appData.noChessProgram) {\r
6248       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6249     }\r
6250     else {\r
6251       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6252     }\r
6253 \r
6254     SetStartupDialogEnables(hDlg);\r
6255     return TRUE;\r
6256 \r
6257   case WM_COMMAND:\r
6258     switch (LOWORD(wParam)) {\r
6259     case IDOK:\r
6260       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6261         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6262         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6263         p = buf;\r
6264         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6265         ParseArgs(StringGet, &p);\r
6266         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6267         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6268         p = buf;\r
6269         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6270         ParseArgs(StringGet, &p);\r
6271         SwapEngines(singleList); // ... and then make it 'second'\r
6272 \r
6273         appData.noChessProgram = FALSE;\r
6274         appData.icsActive = FALSE;\r
6275       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6276         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6277         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6278         p = buf;\r
6279         ParseArgs(StringGet, &p);\r
6280         if (appData.zippyPlay) {\r
6281           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6282           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6283           p = buf;\r
6284           ParseArgs(StringGet, &p);\r
6285         }\r
6286       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6287         appData.noChessProgram = TRUE;\r
6288         appData.icsActive = FALSE;\r
6289       } else {\r
6290         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6291                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6292         return TRUE;\r
6293       }\r
6294       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6295         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6296         p = buf;\r
6297         ParseArgs(StringGet, &p);\r
6298       }\r
6299       EndDialog(hDlg, TRUE);\r
6300       return TRUE;\r
6301 \r
6302     case IDCANCEL:\r
6303       ExitEvent(0);\r
6304       return TRUE;\r
6305 \r
6306     case IDM_HELPCONTENTS:\r
6307       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6308         MessageBox (GetFocus(),\r
6309                     _("Unable to activate help"),\r
6310                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6311       }\r
6312       break;\r
6313 \r
6314     default:\r
6315       SetStartupDialogEnables(hDlg);\r
6316       break;\r
6317     }\r
6318     break;\r
6319   }\r
6320   return FALSE;\r
6321 }\r
6322 \r
6323 /*---------------------------------------------------------------------------*\\r
6324  *\r
6325  * About box dialog functions\r
6326  *\r
6327 \*---------------------------------------------------------------------------*/\r
6328 \r
6329 /* Process messages for "About" dialog box */\r
6330 LRESULT CALLBACK\r
6331 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6332 {\r
6333   switch (message) {\r
6334   case WM_INITDIALOG: /* message: initialize dialog box */\r
6335     /* Center the dialog over the application window */\r
6336     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6337     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6338     Translate(hDlg, ABOUTBOX);\r
6339     JAWS_COPYRIGHT\r
6340     return (TRUE);\r
6341 \r
6342   case WM_COMMAND: /* message: received a command */\r
6343     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6344         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6345       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6346       return (TRUE);\r
6347     }\r
6348     break;\r
6349   }\r
6350   return (FALSE);\r
6351 }\r
6352 \r
6353 /*---------------------------------------------------------------------------*\\r
6354  *\r
6355  * Comment Dialog functions\r
6356  *\r
6357 \*---------------------------------------------------------------------------*/\r
6358 \r
6359 LRESULT CALLBACK\r
6360 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6361 {\r
6362   static HANDLE hwndText = NULL;\r
6363   int len, newSizeX, newSizeY, flags;\r
6364   static int sizeX, sizeY;\r
6365   char *str;\r
6366   RECT rect;\r
6367   MINMAXINFO *mmi;\r
6368 \r
6369   switch (message) {\r
6370   case WM_INITDIALOG: /* message: initialize dialog box */\r
6371     /* Initialize the dialog items */\r
6372     Translate(hDlg, DLG_EditComment);\r
6373     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6374     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6375     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6376     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6377     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6378     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6379     SetWindowText(hDlg, commentTitle);\r
6380     if (editComment) {\r
6381       SetFocus(hwndText);\r
6382     } else {\r
6383       SetFocus(GetDlgItem(hDlg, IDOK));\r
6384     }\r
6385     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6386                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6387                 MAKELPARAM(FALSE, 0));\r
6388     /* Size and position the dialog */\r
6389     if (!commentDialog) {\r
6390       commentDialog = hDlg;\r
6391       flags = SWP_NOZORDER;\r
6392       GetClientRect(hDlg, &rect);\r
6393       sizeX = rect.right;\r
6394       sizeY = rect.bottom;\r
6395       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6396           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6397         WINDOWPLACEMENT wp;\r
6398         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6399         wp.length = sizeof(WINDOWPLACEMENT);\r
6400         wp.flags = 0;\r
6401         wp.showCmd = SW_SHOW;\r
6402         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6403         wp.rcNormalPosition.left = wpComment.x;\r
6404         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6405         wp.rcNormalPosition.top = wpComment.y;\r
6406         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6407         SetWindowPlacement(hDlg, &wp);\r
6408 \r
6409         GetClientRect(hDlg, &rect);\r
6410         newSizeX = rect.right;\r
6411         newSizeY = rect.bottom;\r
6412         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6413                               newSizeX, newSizeY);\r
6414         sizeX = newSizeX;\r
6415         sizeY = newSizeY;\r
6416       }\r
6417     }\r
6418     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6419     return FALSE;\r
6420 \r
6421   case WM_COMMAND: /* message: received a command */\r
6422     switch (LOWORD(wParam)) {\r
6423     case IDOK:\r
6424       if (editComment) {\r
6425         char *p, *q;\r
6426         /* Read changed options from the dialog box */\r
6427         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6428         len = GetWindowTextLength(hwndText);\r
6429         str = (char *) malloc(len + 1);\r
6430         GetWindowText(hwndText, str, len + 1);\r
6431         p = q = str;\r
6432         while (*q) {\r
6433           if (*q == '\r')\r
6434             q++;\r
6435           else\r
6436             *p++ = *q++;\r
6437         }\r
6438         *p = NULLCHAR;\r
6439         ReplaceComment(commentIndex, str);\r
6440         free(str);\r
6441       }\r
6442       CommentPopDown();\r
6443       return TRUE;\r
6444 \r
6445     case IDCANCEL:\r
6446     case OPT_CancelComment:\r
6447       CommentPopDown();\r
6448       return TRUE;\r
6449 \r
6450     case OPT_ClearComment:\r
6451       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6452       break;\r
6453 \r
6454     case OPT_EditComment:\r
6455       EditCommentEvent();\r
6456       return TRUE;\r
6457 \r
6458     default:\r
6459       break;\r
6460     }\r
6461     break;\r
6462 \r
6463   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6464         if( wParam == OPT_CommentText ) {\r
6465             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6466 \r
6467             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6468                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6469                 POINTL pt;\r
6470                 LRESULT index;\r
6471 \r
6472                 pt.x = LOWORD( lpMF->lParam );\r
6473                 pt.y = HIWORD( lpMF->lParam );\r
6474 \r
6475                 if(lpMF->msg == WM_CHAR) {\r
6476                         CHARRANGE sel;\r
6477                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6478                         index = sel.cpMin;\r
6479                 } else\r
6480                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6481 \r
6482                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6483                 len = GetWindowTextLength(hwndText);\r
6484                 str = (char *) malloc(len + 1);\r
6485                 GetWindowText(hwndText, str, len + 1);\r
6486                 ReplaceComment(commentIndex, str);\r
6487                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6488                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6489                 free(str);\r
6490 \r
6491                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6492                 lpMF->msg = WM_USER;\r
6493 \r
6494                 return TRUE;\r
6495             }\r
6496         }\r
6497         break;\r
6498 \r
6499   case WM_SIZE:\r
6500     newSizeX = LOWORD(lParam);\r
6501     newSizeY = HIWORD(lParam);\r
6502     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6503     sizeX = newSizeX;\r
6504     sizeY = newSizeY;\r
6505     break;\r
6506 \r
6507   case WM_GETMINMAXINFO:\r
6508     /* Prevent resizing window too small */\r
6509     mmi = (MINMAXINFO *) lParam;\r
6510     mmi->ptMinTrackSize.x = 100;\r
6511     mmi->ptMinTrackSize.y = 100;\r
6512     break;\r
6513   }\r
6514   return FALSE;\r
6515 }\r
6516 \r
6517 VOID\r
6518 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6519 {\r
6520   FARPROC lpProc;\r
6521   char *p, *q;\r
6522 \r
6523   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6524 \r
6525   if (str == NULL) str = "";\r
6526   p = (char *) malloc(2 * strlen(str) + 2);\r
6527   q = p;\r
6528   while (*str) {\r
6529     if (*str == '\n') *q++ = '\r';\r
6530     *q++ = *str++;\r
6531   }\r
6532   *q = NULLCHAR;\r
6533   if (commentText != NULL) free(commentText);\r
6534 \r
6535   commentIndex = index;\r
6536   commentTitle = title;\r
6537   commentText = p;\r
6538   editComment = edit;\r
6539 \r
6540   if (commentDialog) {\r
6541     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6542     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6543   } else {\r
6544     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6545     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6546                  hwndMain, (DLGPROC)lpProc);\r
6547     FreeProcInstance(lpProc);\r
6548   }\r
6549   commentUp = TRUE;\r
6550 }\r
6551 \r
6552 \r
6553 /*---------------------------------------------------------------------------*\\r
6554  *\r
6555  * Type-in move dialog functions\r
6556  * \r
6557 \*---------------------------------------------------------------------------*/\r
6558 \r
6559 LRESULT CALLBACK\r
6560 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6561 {\r
6562   char move[MSG_SIZ];\r
6563   HWND hInput;\r
6564 \r
6565   switch (message) {\r
6566   case WM_INITDIALOG:\r
6567     move[0] = (char) lParam;\r
6568     move[1] = NULLCHAR;\r
6569     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6570     Translate(hDlg, DLG_TypeInMove);\r
6571     hInput = GetDlgItem(hDlg, OPT_Move);\r
6572     SetWindowText(hInput, move);\r
6573     SetFocus(hInput);\r
6574     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6575     return FALSE;\r
6576 \r
6577   case WM_COMMAND:\r
6578     switch (LOWORD(wParam)) {\r
6579     case IDOK:\r
6580 \r
6581       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6582       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6583       TypeInDoneEvent(move);\r
6584       EndDialog(hDlg, TRUE);\r
6585       return TRUE;\r
6586     case IDCANCEL:\r
6587       EndDialog(hDlg, FALSE);\r
6588       return TRUE;\r
6589     default:\r
6590       break;\r
6591     }\r
6592     break;\r
6593   }\r
6594   return FALSE;\r
6595 }\r
6596 \r
6597 VOID\r
6598 PopUpMoveDialog(char firstchar)\r
6599 {\r
6600     FARPROC lpProc;\r
6601 \r
6602       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6603       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6604         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6605       FreeProcInstance(lpProc);\r
6606 }\r
6607 \r
6608 /*---------------------------------------------------------------------------*\\r
6609  *\r
6610  * Type-in name dialog functions\r
6611  * \r
6612 \*---------------------------------------------------------------------------*/\r
6613 \r
6614 LRESULT CALLBACK\r
6615 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6616 {\r
6617   char move[MSG_SIZ];\r
6618   HWND hInput;\r
6619 \r
6620   switch (message) {\r
6621   case WM_INITDIALOG:\r
6622     move[0] = (char) lParam;\r
6623     move[1] = NULLCHAR;\r
6624     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6625     Translate(hDlg, DLG_TypeInName);\r
6626     hInput = GetDlgItem(hDlg, OPT_Name);\r
6627     SetWindowText(hInput, move);\r
6628     SetFocus(hInput);\r
6629     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6630     return FALSE;\r
6631 \r
6632   case WM_COMMAND:\r
6633     switch (LOWORD(wParam)) {\r
6634     case IDOK:\r
6635       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6636       appData.userName = strdup(move);\r
6637       SetUserLogo();\r
6638       SetGameInfo();\r
6639       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6640         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6641         DisplayTitle(move);\r
6642       }\r
6643 \r
6644 \r
6645       EndDialog(hDlg, TRUE);\r
6646       return TRUE;\r
6647     case IDCANCEL:\r
6648       EndDialog(hDlg, FALSE);\r
6649       return TRUE;\r
6650     default:\r
6651       break;\r
6652     }\r
6653     break;\r
6654   }\r
6655   return FALSE;\r
6656 }\r
6657 \r
6658 VOID\r
6659 PopUpNameDialog(char firstchar)\r
6660 {\r
6661     FARPROC lpProc;\r
6662     \r
6663       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6664       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6665         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6666       FreeProcInstance(lpProc);\r
6667 }\r
6668 \r
6669 /*---------------------------------------------------------------------------*\\r
6670  *\r
6671  *  Error dialogs\r
6672  * \r
6673 \*---------------------------------------------------------------------------*/\r
6674 \r
6675 /* Nonmodal error box */\r
6676 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6677                              WPARAM wParam, LPARAM lParam);\r
6678 \r
6679 VOID\r
6680 ErrorPopUp(char *title, char *content)\r
6681 {\r
6682   FARPROC lpProc;\r
6683   char *p, *q;\r
6684   BOOLEAN modal = hwndMain == NULL;\r
6685 \r
6686   p = content;\r
6687   q = errorMessage;\r
6688   while (*p) {\r
6689     if (*p == '\n') {\r
6690       if (modal) {\r
6691         *q++ = ' ';\r
6692         p++;\r
6693       } else {\r
6694         *q++ = '\r';\r
6695         *q++ = *p++;\r
6696       }\r
6697     } else {\r
6698       *q++ = *p++;\r
6699     }\r
6700   }\r
6701   *q = NULLCHAR;\r
6702   strncpy(errorTitle, title, sizeof(errorTitle));\r
6703   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6704   \r
6705   if (modal) {\r
6706     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6707   } else {\r
6708     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6709     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6710                  hwndMain, (DLGPROC)lpProc);\r
6711     FreeProcInstance(lpProc);\r
6712   }\r
6713 }\r
6714 \r
6715 VOID\r
6716 ErrorPopDown()\r
6717 {\r
6718   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6719   if (errorDialog == NULL) return;\r
6720   DestroyWindow(errorDialog);\r
6721   errorDialog = NULL;\r
6722   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6723 }\r
6724 \r
6725 LRESULT CALLBACK\r
6726 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6727 {\r
6728   HANDLE hwndText;\r
6729   RECT rChild;\r
6730 \r
6731   switch (message) {\r
6732   case WM_INITDIALOG:\r
6733     GetWindowRect(hDlg, &rChild);\r
6734 \r
6735     /*\r
6736     SetWindowPos(hDlg, NULL, rChild.left,\r
6737       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6738       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6739     */\r
6740 \r
6741     /* \r
6742         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6743         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6744         and it doesn't work when you resize the dialog.\r
6745         For now, just give it a default position.\r
6746     */\r
6747     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6748     Translate(hDlg, DLG_Error);\r
6749 \r
6750     errorDialog = hDlg;\r
6751     SetWindowText(hDlg, errorTitle);\r
6752     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6753     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6754     return FALSE;\r
6755 \r
6756   case WM_COMMAND:\r
6757     switch (LOWORD(wParam)) {\r
6758     case IDOK:\r
6759     case IDCANCEL:\r
6760       if (errorDialog == hDlg) errorDialog = NULL;\r
6761       DestroyWindow(hDlg);\r
6762       return TRUE;\r
6763 \r
6764     default:\r
6765       break;\r
6766     }\r
6767     break;\r
6768   }\r
6769   return FALSE;\r
6770 }\r
6771 \r
6772 #ifdef GOTHIC\r
6773 HWND gothicDialog = NULL;\r
6774 \r
6775 LRESULT CALLBACK\r
6776 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6777 {\r
6778   HANDLE hwndText;\r
6779   RECT rChild;\r
6780   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6781 \r
6782   switch (message) {\r
6783   case WM_INITDIALOG:\r
6784     GetWindowRect(hDlg, &rChild);\r
6785 \r
6786     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6787                                                              SWP_NOZORDER);\r
6788 \r
6789     /* \r
6790         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6791         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6792         and it doesn't work when you resize the dialog.\r
6793         For now, just give it a default position.\r
6794     */\r
6795     gothicDialog = hDlg;\r
6796     SetWindowText(hDlg, errorTitle);\r
6797     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6798     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6799     return FALSE;\r
6800 \r
6801   case WM_COMMAND:\r
6802     switch (LOWORD(wParam)) {\r
6803     case IDOK:\r
6804     case IDCANCEL:\r
6805       if (errorDialog == hDlg) errorDialog = NULL;\r
6806       DestroyWindow(hDlg);\r
6807       return TRUE;\r
6808 \r
6809     default:\r
6810       break;\r
6811     }\r
6812     break;\r
6813   }\r
6814   return FALSE;\r
6815 }\r
6816 \r
6817 VOID\r
6818 GothicPopUp(char *title, VariantClass variant)\r
6819 {\r
6820   FARPROC lpProc;\r
6821   static char *lastTitle;\r
6822 \r
6823   strncpy(errorTitle, title, sizeof(errorTitle));\r
6824   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6825 \r
6826   if(lastTitle != title && gothicDialog != NULL) {\r
6827     DestroyWindow(gothicDialog);\r
6828     gothicDialog = NULL;\r
6829   }\r
6830   if(variant != VariantNormal && gothicDialog == NULL) {\r
6831     title = lastTitle;\r
6832     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6833     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6834                  hwndMain, (DLGPROC)lpProc);\r
6835     FreeProcInstance(lpProc);\r
6836   }\r
6837 }\r
6838 #endif\r
6839 \r
6840 /*---------------------------------------------------------------------------*\\r
6841  *\r
6842  *  Ics Interaction console functions\r
6843  *\r
6844 \*---------------------------------------------------------------------------*/\r
6845 \r
6846 #define HISTORY_SIZE 64\r
6847 static char *history[HISTORY_SIZE];\r
6848 int histIn = 0, histP = 0;\r
6849 \r
6850 VOID\r
6851 SaveInHistory(char *cmd)\r
6852 {\r
6853   if (history[histIn] != NULL) {\r
6854     free(history[histIn]);\r
6855     history[histIn] = NULL;\r
6856   }\r
6857   if (*cmd == NULLCHAR) return;\r
6858   history[histIn] = StrSave(cmd);\r
6859   histIn = (histIn + 1) % HISTORY_SIZE;\r
6860   if (history[histIn] != NULL) {\r
6861     free(history[histIn]);\r
6862 \r
6863     history[histIn] = NULL;\r
6864   }\r
6865   histP = histIn;\r
6866 }\r
6867 \r
6868 char *\r
6869 PrevInHistory(char *cmd)\r
6870 {\r
6871   int newhp;\r
6872   if (histP == histIn) {\r
6873     if (history[histIn] != NULL) free(history[histIn]);\r
6874     history[histIn] = StrSave(cmd);\r
6875   }\r
6876   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6877   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6878   histP = newhp;\r
6879   return history[histP];\r
6880 }\r
6881 \r
6882 char *\r
6883 NextInHistory()\r
6884 {\r
6885   if (histP == histIn) return NULL;\r
6886   histP = (histP + 1) % HISTORY_SIZE;\r
6887   return history[histP];   \r
6888 }\r
6889 \r
6890 HMENU\r
6891 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6892 {\r
6893   HMENU hmenu, h;\r
6894   int i = 0;\r
6895   hmenu = LoadMenu(hInst, "TextMenu");\r
6896   h = GetSubMenu(hmenu, 0);\r
6897   while (e->item) {\r
6898     if (strcmp(e->item, "-") == 0) {\r
6899       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6900     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6901       int flags = MF_STRING, j = 0;\r
6902       if (e->item[0] == '|') {\r
6903         flags |= MF_MENUBARBREAK;\r
6904         j++;\r
6905       }\r
6906       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6907       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6908     }\r
6909     e++;\r
6910     i++;\r
6911   } \r
6912   return hmenu;\r
6913 }\r
6914 \r
6915 WNDPROC consoleTextWindowProc;\r
6916 \r
6917 void\r
6918 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6919 {\r
6920   char buf[MSG_SIZ], name[MSG_SIZ];\r
6921   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6922   CHARRANGE sel;\r
6923 \r
6924   if (!getname) {\r
6925     SetWindowText(hInput, command);\r
6926     if (immediate) {\r
6927       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6928     } else {\r
6929       sel.cpMin = 999999;\r
6930       sel.cpMax = 999999;\r
6931       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6932       SetFocus(hInput);\r
6933     }\r
6934     return;\r
6935   }    \r
6936   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6937   if (sel.cpMin == sel.cpMax) {\r
6938     /* Expand to surrounding word */\r
6939     TEXTRANGE tr;\r
6940     do {\r
6941       tr.chrg.cpMax = sel.cpMin;\r
6942       tr.chrg.cpMin = --sel.cpMin;\r
6943       if (sel.cpMin < 0) break;\r
6944       tr.lpstrText = name;\r
6945       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6946     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6947     sel.cpMin++;\r
6948 \r
6949     do {\r
6950       tr.chrg.cpMin = sel.cpMax;\r
6951       tr.chrg.cpMax = ++sel.cpMax;\r
6952       tr.lpstrText = name;\r
6953       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6954     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6955     sel.cpMax--;\r
6956 \r
6957     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6958       MessageBeep(MB_ICONEXCLAMATION);\r
6959       return;\r
6960     }\r
6961     tr.chrg = sel;\r
6962     tr.lpstrText = name;\r
6963     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6964   } else {\r
6965     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6966       MessageBeep(MB_ICONEXCLAMATION);\r
6967       return;\r
6968     }\r
6969     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6970   }\r
6971   if (immediate) {\r
6972     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6973     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6974     SetWindowText(hInput, buf);\r
6975     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6976   } else {\r
6977     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6978       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6979     SetWindowText(hInput, buf);\r
6980     sel.cpMin = 999999;\r
6981     sel.cpMax = 999999;\r
6982     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6983     SetFocus(hInput);\r
6984   }\r
6985 }\r
6986 \r
6987 LRESULT CALLBACK \r
6988 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6989 {\r
6990   HWND hInput;\r
6991   CHARRANGE sel;\r
6992 \r
6993   switch (message) {\r
6994   case WM_KEYDOWN:\r
6995     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6996     if(wParam=='R') return 0;\r
6997     switch (wParam) {\r
6998     case VK_PRIOR:\r
6999       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7000       return 0;\r
7001     case VK_NEXT:\r
7002       sel.cpMin = 999999;\r
7003       sel.cpMax = 999999;\r
7004       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7005       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7006       return 0;\r
7007     }\r
7008     break;\r
7009   case WM_CHAR:\r
7010    if(wParam != '\022') {\r
7011     if (wParam == '\t') {\r
7012       if (GetKeyState(VK_SHIFT) < 0) {\r
7013         /* shifted */\r
7014         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7015         if (buttonDesc[0].hwnd) {\r
7016           SetFocus(buttonDesc[0].hwnd);\r
7017         } else {\r
7018           SetFocus(hwndMain);\r
7019         }\r
7020       } else {\r
7021         /* unshifted */\r
7022         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7023       }\r
7024     } else {\r
7025       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7026       JAWS_DELETE( SetFocus(hInput); )\r
7027       SendMessage(hInput, message, wParam, lParam);\r
7028     }\r
7029     return 0;\r
7030    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
7031    lParam = -1;\r
7032   case WM_RBUTTONDOWN:\r
7033     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7034       /* Move selection here if it was empty */\r
7035       POINT pt;\r
7036       pt.x = LOWORD(lParam);\r
7037       pt.y = HIWORD(lParam);\r
7038       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7039       if (sel.cpMin == sel.cpMax) {\r
7040         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7041         sel.cpMax = sel.cpMin;\r
7042         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7043       }\r
7044       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7045 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
7046       POINT pt;\r
7047       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7048       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7049       if (sel.cpMin == sel.cpMax) {\r
7050         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7051         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7052       }\r
7053       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7054         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7055       }\r
7056       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
7057       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
7058       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
7059       MenuPopup(hwnd, pt, hmenu, -1);\r
7060 }\r
7061     }\r
7062     return 0;\r
7063   case WM_RBUTTONUP:\r
7064     if (GetKeyState(VK_SHIFT) & ~1) {\r
7065       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7066         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7067     }\r
7068     return 0;\r
7069   case WM_PASTE:\r
7070     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7071     SetFocus(hInput);\r
7072     return SendMessage(hInput, message, wParam, lParam);\r
7073   case WM_MBUTTONDOWN:\r
7074     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7075   case WM_COMMAND:\r
7076     switch (LOWORD(wParam)) {\r
7077     case IDM_QuickPaste:\r
7078       {\r
7079         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7080         if (sel.cpMin == sel.cpMax) {\r
7081           MessageBeep(MB_ICONEXCLAMATION);\r
7082           return 0;\r
7083         }\r
7084         SendMessage(hwnd, WM_COPY, 0, 0);\r
7085         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7086         SendMessage(hInput, WM_PASTE, 0, 0);\r
7087         SetFocus(hInput);\r
7088         return 0;\r
7089       }\r
7090     case IDM_Cut:\r
7091       SendMessage(hwnd, WM_CUT, 0, 0);\r
7092       return 0;\r
7093     case IDM_Paste:\r
7094       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7095       return 0;\r
7096     case IDM_Copy:\r
7097       SendMessage(hwnd, WM_COPY, 0, 0);\r
7098       return 0;\r
7099     default:\r
7100       {\r
7101         int i = LOWORD(wParam) - IDM_CommandX;\r
7102         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7103             icsTextMenuEntry[i].command != NULL) {\r
7104           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7105                    icsTextMenuEntry[i].getname,\r
7106                    icsTextMenuEntry[i].immediate);\r
7107           return 0;\r
7108         }\r
7109       }\r
7110       break;\r
7111     }\r
7112     break;\r
7113   }\r
7114   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7115 }\r
7116 \r
7117 WNDPROC consoleInputWindowProc;\r
7118 \r
7119 LRESULT CALLBACK\r
7120 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7121 {\r
7122   char buf[MSG_SIZ];\r
7123   char *p;\r
7124   static BOOL sendNextChar = FALSE;\r
7125   static BOOL quoteNextChar = FALSE;\r
7126   InputSource *is = consoleInputSource;\r
7127   CHARFORMAT cf;\r
7128   CHARRANGE sel;\r
7129 \r
7130   switch (message) {\r
7131   case WM_CHAR:\r
7132     if (!appData.localLineEditing || sendNextChar) {\r
7133       is->buf[0] = (CHAR) wParam;\r
7134       is->count = 1;\r
7135       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7136       sendNextChar = FALSE;\r
7137       return 0;\r
7138     }\r
7139     if (quoteNextChar) {\r
7140       buf[0] = (char) wParam;\r
7141       buf[1] = NULLCHAR;\r
7142       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7143       quoteNextChar = FALSE;\r
7144       return 0;\r
7145     }\r
7146     switch (wParam) {\r
7147     case '\r':   /* Enter key */\r
7148       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7149       if (consoleEcho) SaveInHistory(is->buf);\r
7150       is->buf[is->count++] = '\n';\r
7151       is->buf[is->count] = NULLCHAR;\r
7152       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7153       if (consoleEcho) {\r
7154         ConsoleOutput(is->buf, is->count, TRUE);\r
7155       } else if (appData.localLineEditing) {\r
7156         ConsoleOutput("\n", 1, TRUE);\r
7157       }\r
7158       /* fall thru */\r
7159     case '\033': /* Escape key */\r
7160       SetWindowText(hwnd, "");\r
7161       cf.cbSize = sizeof(CHARFORMAT);\r
7162       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7163       if (consoleEcho) {\r
7164         cf.crTextColor = textAttribs[ColorNormal].color;\r
7165       } else {\r
7166         cf.crTextColor = COLOR_ECHOOFF;\r
7167       }\r
7168       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7169       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7170       return 0;\r
7171     case '\t':   /* Tab key */\r
7172       if (GetKeyState(VK_SHIFT) < 0) {\r
7173         /* shifted */\r
7174         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7175       } else {\r
7176         /* unshifted */\r
7177         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7178         if (buttonDesc[0].hwnd) {\r
7179           SetFocus(buttonDesc[0].hwnd);\r
7180         } else {\r
7181           SetFocus(hwndMain);\r
7182         }\r
7183       }\r
7184       return 0;\r
7185     case '\023': /* Ctrl+S */\r
7186       sendNextChar = TRUE;\r
7187       return 0;\r
7188     case '\021': /* Ctrl+Q */\r
7189       quoteNextChar = TRUE;\r
7190       return 0;\r
7191     JAWS_REPLAY\r
7192     default:\r
7193       break;\r
7194     }\r
7195     break;\r
7196   case WM_KEYDOWN:\r
7197     switch (wParam) {\r
7198     case VK_UP:\r
7199       GetWindowText(hwnd, buf, MSG_SIZ);\r
7200       p = PrevInHistory(buf);\r
7201       if (p != NULL) {\r
7202         SetWindowText(hwnd, p);\r
7203         sel.cpMin = 999999;\r
7204         sel.cpMax = 999999;\r
7205         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7206         return 0;\r
7207       }\r
7208       break;\r
7209     case VK_DOWN:\r
7210       p = NextInHistory();\r
7211       if (p != NULL) {\r
7212         SetWindowText(hwnd, p);\r
7213         sel.cpMin = 999999;\r
7214         sel.cpMax = 999999;\r
7215         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7216         return 0;\r
7217       }\r
7218       break;\r
7219     case VK_HOME:\r
7220     case VK_END:\r
7221       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7222       /* fall thru */\r
7223     case VK_PRIOR:\r
7224     case VK_NEXT:\r
7225       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7226       return 0;\r
7227     }\r
7228     break;\r
7229   case WM_MBUTTONDOWN:\r
7230     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7231       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7232     break;\r
7233   case WM_RBUTTONUP:\r
7234     if (GetKeyState(VK_SHIFT) & ~1) {\r
7235       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7236         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7237     } else {\r
7238       POINT pt;\r
7239       HMENU hmenu;\r
7240       hmenu = LoadMenu(hInst, "InputMenu");\r
7241       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7242       if (sel.cpMin == sel.cpMax) {\r
7243         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7244         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7245       }\r
7246       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7247         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7248       }\r
7249       pt.x = LOWORD(lParam);\r
7250       pt.y = HIWORD(lParam);\r
7251       MenuPopup(hwnd, pt, hmenu, -1);\r
7252     }\r
7253     return 0;\r
7254   case WM_COMMAND:\r
7255     switch (LOWORD(wParam)) { \r
7256     case IDM_Undo:\r
7257       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7258       return 0;\r
7259     case IDM_SelectAll:\r
7260       sel.cpMin = 0;\r
7261       sel.cpMax = -1; /*999999?*/\r
7262       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7263       return 0;\r
7264     case IDM_Cut:\r
7265       SendMessage(hwnd, WM_CUT, 0, 0);\r
7266       return 0;\r
7267     case IDM_Paste:\r
7268       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7269       return 0;\r
7270     case IDM_Copy:\r
7271       SendMessage(hwnd, WM_COPY, 0, 0);\r
7272       return 0;\r
7273     }\r
7274     break;\r
7275   }\r
7276   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7277 }\r
7278 \r
7279 #define CO_MAX  100000\r
7280 #define CO_TRIM   1000\r
7281 \r
7282 LRESULT CALLBACK\r
7283 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7284 {\r
7285   static SnapData sd;\r
7286   HWND hText, hInput;\r
7287   RECT rect;\r
7288   static int sizeX, sizeY;\r
7289   int newSizeX, newSizeY;\r
7290   MINMAXINFO *mmi;\r
7291   WORD wMask;\r
7292 \r
7293   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7294   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7295 \r
7296   switch (message) {\r
7297   case WM_NOTIFY:\r
7298     if (((NMHDR*)lParam)->code == EN_LINK)\r
7299     {\r
7300       ENLINK *pLink = (ENLINK*)lParam;\r
7301       if (pLink->msg == WM_LBUTTONUP)\r
7302       {\r
7303         TEXTRANGE tr;\r
7304 \r
7305         tr.chrg = pLink->chrg;\r
7306         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7307         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7308         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7309         free(tr.lpstrText);\r
7310       }\r
7311     }\r
7312     break;\r
7313   case WM_INITDIALOG: /* message: initialize dialog box */\r
7314     hwndConsole = hDlg;\r
7315     SetFocus(hInput);\r
7316     consoleTextWindowProc = (WNDPROC)\r
7317       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7318     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7319     consoleInputWindowProc = (WNDPROC)\r
7320       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7321     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7322     Colorize(ColorNormal, TRUE);\r
7323     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7324     ChangedConsoleFont();\r
7325     GetClientRect(hDlg, &rect);\r
7326     sizeX = rect.right;\r
7327     sizeY = rect.bottom;\r
7328     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7329         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7330       WINDOWPLACEMENT wp;\r
7331       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7332       wp.length = sizeof(WINDOWPLACEMENT);\r
7333       wp.flags = 0;\r
7334       wp.showCmd = SW_SHOW;\r
7335       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7336       wp.rcNormalPosition.left = wpConsole.x;\r
7337       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7338       wp.rcNormalPosition.top = wpConsole.y;\r
7339       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7340       SetWindowPlacement(hDlg, &wp);\r
7341     }\r
7342 \r
7343    // [HGM] Chessknight's change 2004-07-13\r
7344    else { /* Determine Defaults */\r
7345        WINDOWPLACEMENT wp;\r
7346        wpConsole.x = wpMain.width + 1;\r
7347        wpConsole.y = wpMain.y;\r
7348        wpConsole.width = screenWidth -  wpMain.width;\r
7349        wpConsole.height = wpMain.height;\r
7350        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7351        wp.length = sizeof(WINDOWPLACEMENT);\r
7352        wp.flags = 0;\r
7353        wp.showCmd = SW_SHOW;\r
7354        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7355        wp.rcNormalPosition.left = wpConsole.x;\r
7356        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7357        wp.rcNormalPosition.top = wpConsole.y;\r
7358        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7359        SetWindowPlacement(hDlg, &wp);\r
7360     }\r
7361 \r
7362    // Allow hText to highlight URLs and send notifications on them\r
7363    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7364    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7365    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7366    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7367 \r
7368     return FALSE;\r
7369 \r
7370   case WM_SETFOCUS:\r
7371     SetFocus(hInput);\r
7372     return 0;\r
7373 \r
7374   case WM_CLOSE:\r
7375     ExitEvent(0);\r
7376     /* not reached */\r
7377     break;\r
7378 \r
7379   case WM_SIZE:\r
7380     if (IsIconic(hDlg)) break;\r
7381     newSizeX = LOWORD(lParam);\r
7382     newSizeY = HIWORD(lParam);\r
7383     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7384       RECT rectText, rectInput;\r
7385       POINT pt;\r
7386       int newTextHeight, newTextWidth;\r
7387       GetWindowRect(hText, &rectText);\r
7388       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7389       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7390       if (newTextHeight < 0) {\r
7391         newSizeY += -newTextHeight;\r
7392         newTextHeight = 0;\r
7393       }\r
7394       SetWindowPos(hText, NULL, 0, 0,\r
7395         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7396       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7397       pt.x = rectInput.left;\r
7398       pt.y = rectInput.top + newSizeY - sizeY;\r
7399       ScreenToClient(hDlg, &pt);\r
7400       SetWindowPos(hInput, NULL, \r
7401         pt.x, pt.y, /* needs client coords */   \r
7402         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7403         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7404     }\r
7405     sizeX = newSizeX;\r
7406     sizeY = newSizeY;\r
7407     break;\r
7408 \r
7409   case WM_GETMINMAXINFO:\r
7410     /* Prevent resizing window too small */\r
7411     mmi = (MINMAXINFO *) lParam;\r
7412     mmi->ptMinTrackSize.x = 100;\r
7413     mmi->ptMinTrackSize.y = 100;\r
7414     break;\r
7415 \r
7416   /* [AS] Snapping */\r
7417   case WM_ENTERSIZEMOVE:\r
7418     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7419 \r
7420   case WM_SIZING:\r
7421     return OnSizing( &sd, hDlg, wParam, lParam );\r
7422 \r
7423   case WM_MOVING:\r
7424     return OnMoving( &sd, hDlg, wParam, lParam );\r
7425 \r
7426   case WM_EXITSIZEMOVE:\r
7427         UpdateICSWidth(hText);\r
7428     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7429   }\r
7430 \r
7431   return DefWindowProc(hDlg, message, wParam, lParam);\r
7432 }\r
7433 \r
7434 \r
7435 VOID\r
7436 ConsoleCreate()\r
7437 {\r
7438   HWND hCons;\r
7439   if (hwndConsole) return;\r
7440   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7441   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7442 }\r
7443 \r
7444 \r
7445 VOID\r
7446 ConsoleOutput(char* data, int length, int forceVisible)\r
7447 {\r
7448   HWND hText;\r
7449   int trim, exlen;\r
7450   char *p, *q;\r
7451   char buf[CO_MAX+1];\r
7452   POINT pEnd;\r
7453   RECT rect;\r
7454   static int delayLF = 0;\r
7455   CHARRANGE savesel, sel;\r
7456 \r
7457   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7458   p = data;\r
7459   q = buf;\r
7460   if (delayLF) {\r
7461     *q++ = '\r';\r
7462     *q++ = '\n';\r
7463     delayLF = 0;\r
7464   }\r
7465   while (length--) {\r
7466     if (*p == '\n') {\r
7467       if (*++p) {\r
7468         *q++ = '\r';\r
7469         *q++ = '\n';\r
7470       } else {\r
7471         delayLF = 1;\r
7472       }\r
7473     } else if (*p == '\007') {\r
7474        MyPlaySound(&sounds[(int)SoundBell]);\r
7475        p++;\r
7476     } else {\r
7477       *q++ = *p++;\r
7478     }\r
7479   }\r
7480   *q = NULLCHAR;\r
7481   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7482   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7483   /* Save current selection */\r
7484   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7485   exlen = GetWindowTextLength(hText);\r
7486   /* Find out whether current end of text is visible */\r
7487   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7488   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7489   /* Trim existing text if it's too long */\r
7490   if (exlen + (q - buf) > CO_MAX) {\r
7491     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7492     sel.cpMin = 0;\r
7493     sel.cpMax = trim;\r
7494     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7495     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7496     exlen -= trim;\r
7497     savesel.cpMin -= trim;\r
7498     savesel.cpMax -= trim;\r
7499     if (exlen < 0) exlen = 0;\r
7500     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7501     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7502   }\r
7503   /* Append the new text */\r
7504   sel.cpMin = exlen;\r
7505   sel.cpMax = exlen;\r
7506   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7507   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7508   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7509   if (forceVisible || exlen == 0 ||\r
7510       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7511        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7512     /* Scroll to make new end of text visible if old end of text\r
7513        was visible or new text is an echo of user typein */\r
7514     sel.cpMin = 9999999;\r
7515     sel.cpMax = 9999999;\r
7516     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7517     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7518     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7519     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7520   }\r
7521   if (savesel.cpMax == exlen || forceVisible) {\r
7522     /* Move insert point to new end of text if it was at the old\r
7523        end of text or if the new text is an echo of user typein */\r
7524     sel.cpMin = 9999999;\r
7525     sel.cpMax = 9999999;\r
7526     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7527   } else {\r
7528     /* Restore previous selection */\r
7529     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7530   }\r
7531   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7532 }\r
7533 \r
7534 /*---------*/\r
7535 \r
7536 \r
7537 void\r
7538 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7539 {\r
7540   char buf[100];\r
7541   char *str;\r
7542   COLORREF oldFg, oldBg;\r
7543   HFONT oldFont;\r
7544   RECT rect;\r
7545 \r
7546   if(copyNumber > 1)\r
7547     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7548 \r
7549   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7550   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7551   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7552 \r
7553   rect.left = x;\r
7554   rect.right = x + squareSize;\r
7555   rect.top  = y;\r
7556   rect.bottom = y + squareSize;\r
7557   str = buf;\r
7558 \r
7559   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7560                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7561              y, ETO_CLIPPED|ETO_OPAQUE,\r
7562              &rect, str, strlen(str), NULL);\r
7563 \r
7564   (void) SetTextColor(hdc, oldFg);\r
7565   (void) SetBkColor(hdc, oldBg);\r
7566   (void) SelectObject(hdc, oldFont);\r
7567 }\r
7568 \r
7569 void\r
7570 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7571               RECT *rect, char *color, char *flagFell)\r
7572 {\r
7573   char buf[100];\r
7574   char *str;\r
7575   COLORREF oldFg, oldBg;\r
7576   HFONT oldFont;\r
7577 \r
7578   if (twoBoards && partnerUp) return;\r
7579   if (appData.clockMode) {\r
7580     if (tinyLayout)\r
7581       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7582     else\r
7583       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7584     str = buf;\r
7585   } else {\r
7586     str = color;\r
7587   }\r
7588 \r
7589   if (highlight) {\r
7590     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7591     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7592   } else {\r
7593     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7594     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7595   }\r
7596   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7597 \r
7598   JAWS_SILENCE\r
7599 \r
7600   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7601              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7602              rect, str, strlen(str), NULL);\r
7603   if(logoHeight > 0 && appData.clockMode) {\r
7604       RECT r;\r
7605       str += strlen(color)+2;\r
7606       r.top = rect->top + logoHeight/2;\r
7607       r.left = rect->left;\r
7608       r.right = rect->right;\r
7609       r.bottom = rect->bottom;\r
7610       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7611                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7612                  &r, str, strlen(str), NULL);\r
7613   }\r
7614   (void) SetTextColor(hdc, oldFg);\r
7615   (void) SetBkColor(hdc, oldBg);\r
7616   (void) SelectObject(hdc, oldFont);\r
7617 }\r
7618 \r
7619 \r
7620 int\r
7621 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7622            OVERLAPPED *ovl)\r
7623 {\r
7624   int ok, err;\r
7625 \r
7626   /* [AS]  */\r
7627   if( count <= 0 ) {\r
7628     if (appData.debugMode) {\r
7629       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7630     }\r
7631 \r
7632     return ERROR_INVALID_USER_BUFFER;\r
7633   }\r
7634 \r
7635   ResetEvent(ovl->hEvent);\r
7636   ovl->Offset = ovl->OffsetHigh = 0;\r
7637   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7638   if (ok) {\r
7639     err = NO_ERROR;\r
7640   } else {\r
7641     err = GetLastError();\r
7642     if (err == ERROR_IO_PENDING) {\r
7643       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7644       if (ok)\r
7645         err = NO_ERROR;\r
7646       else\r
7647         err = GetLastError();\r
7648     }\r
7649   }\r
7650   return err;\r
7651 }\r
7652 \r
7653 int\r
7654 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7655             OVERLAPPED *ovl)\r
7656 {\r
7657   int ok, err;\r
7658 \r
7659   ResetEvent(ovl->hEvent);\r
7660   ovl->Offset = ovl->OffsetHigh = 0;\r
7661   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7662   if (ok) {\r
7663     err = NO_ERROR;\r
7664   } else {\r
7665     err = GetLastError();\r
7666     if (err == ERROR_IO_PENDING) {\r
7667       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7668       if (ok)\r
7669         err = NO_ERROR;\r
7670       else\r
7671         err = GetLastError();\r
7672     }\r
7673   }\r
7674   return err;\r
7675 }\r
7676 \r
7677 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7678 void CheckForInputBufferFull( InputSource * is )\r
7679 {\r
7680     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7681         /* Look for end of line */\r
7682         char * p = is->buf;\r
7683         \r
7684         while( p < is->next && *p != '\n' ) {\r
7685             p++;\r
7686         }\r
7687 \r
7688         if( p >= is->next ) {\r
7689             if (appData.debugMode) {\r
7690                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7691             }\r
7692 \r
7693             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7694             is->count = (DWORD) -1;\r
7695             is->next = is->buf;\r
7696         }\r
7697     }\r
7698 }\r
7699 \r
7700 DWORD\r
7701 InputThread(LPVOID arg)\r
7702 {\r
7703   InputSource *is;\r
7704   OVERLAPPED ovl;\r
7705 \r
7706   is = (InputSource *) arg;\r
7707   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7708   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7709   while (is->hThread != NULL) {\r
7710     is->error = DoReadFile(is->hFile, is->next,\r
7711                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7712                            &is->count, &ovl);\r
7713     if (is->error == NO_ERROR) {\r
7714       is->next += is->count;\r
7715     } else {\r
7716       if (is->error == ERROR_BROKEN_PIPE) {\r
7717         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7718         is->count = 0;\r
7719       } else {\r
7720         is->count = (DWORD) -1;\r
7721         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7722         break; \r
7723       }\r
7724     }\r
7725 \r
7726     CheckForInputBufferFull( is );\r
7727 \r
7728     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7729 \r
7730     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7731 \r
7732     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7733   }\r
7734 \r
7735   CloseHandle(ovl.hEvent);\r
7736   CloseHandle(is->hFile);\r
7737 \r
7738   if (appData.debugMode) {\r
7739     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7740   }\r
7741 \r
7742   return 0;\r
7743 }\r
7744 \r
7745 \r
7746 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7747 DWORD\r
7748 NonOvlInputThread(LPVOID arg)\r
7749 {\r
7750   InputSource *is;\r
7751   char *p, *q;\r
7752   int i;\r
7753   char prev;\r
7754 \r
7755   is = (InputSource *) arg;\r
7756   while (is->hThread != NULL) {\r
7757     is->error = ReadFile(is->hFile, is->next,\r
7758                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7759                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7760     if (is->error == NO_ERROR) {\r
7761       /* Change CRLF to LF */\r
7762       if (is->next > is->buf) {\r
7763         p = is->next - 1;\r
7764         i = is->count + 1;\r
7765       } else {\r
7766         p = is->next;\r
7767         i = is->count;\r
7768       }\r
7769       q = p;\r
7770       prev = NULLCHAR;\r
7771       while (i > 0) {\r
7772         if (prev == '\r' && *p == '\n') {\r
7773           *(q-1) = '\n';\r
7774           is->count--;\r
7775         } else { \r
7776           *q++ = *p;\r
7777         }\r
7778         prev = *p++;\r
7779         i--;\r
7780       }\r
7781       *q = NULLCHAR;\r
7782       is->next = q;\r
7783     } else {\r
7784       if (is->error == ERROR_BROKEN_PIPE) {\r
7785         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7786         is->count = 0; \r
7787       } else {\r
7788         is->count = (DWORD) -1;\r
7789       }\r
7790     }\r
7791 \r
7792     CheckForInputBufferFull( is );\r
7793 \r
7794     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7795 \r
7796     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7797 \r
7798     if (is->count < 0) break;  /* Quit on error */\r
7799   }\r
7800   CloseHandle(is->hFile);\r
7801   return 0;\r
7802 }\r
7803 \r
7804 DWORD\r
7805 SocketInputThread(LPVOID arg)\r
7806 {\r
7807   InputSource *is;\r
7808 \r
7809   is = (InputSource *) arg;\r
7810   while (is->hThread != NULL) {\r
7811     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7812     if ((int)is->count == SOCKET_ERROR) {\r
7813       is->count = (DWORD) -1;\r
7814       is->error = WSAGetLastError();\r
7815     } else {\r
7816       is->error = NO_ERROR;\r
7817       is->next += is->count;\r
7818       if (is->count == 0 && is->second == is) {\r
7819         /* End of file on stderr; quit with no message */\r
7820         break;\r
7821       }\r
7822     }\r
7823     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7824 \r
7825     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7826 \r
7827     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7828   }\r
7829   return 0;\r
7830 }\r
7831 \r
7832 VOID\r
7833 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7834 {\r
7835   InputSource *is;\r
7836 \r
7837   is = (InputSource *) lParam;\r
7838   if (is->lineByLine) {\r
7839     /* Feed in lines one by one */\r
7840     char *p = is->buf;\r
7841     char *q = p;\r
7842     while (q < is->next) {\r
7843       if (*q++ == '\n') {\r
7844         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7845         p = q;\r
7846       }\r
7847     }\r
7848     \r
7849     /* Move any partial line to the start of the buffer */\r
7850     q = is->buf;\r
7851     while (p < is->next) {\r
7852       *q++ = *p++;\r
7853     }\r
7854     is->next = q;\r
7855 \r
7856     if (is->error != NO_ERROR || is->count == 0) {\r
7857       /* Notify backend of the error.  Note: If there was a partial\r
7858          line at the end, it is not flushed through. */\r
7859       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7860     }\r
7861   } else {\r
7862     /* Feed in the whole chunk of input at once */\r
7863     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7864     is->next = is->buf;\r
7865   }\r
7866 }\r
7867 \r
7868 /*---------------------------------------------------------------------------*\\r
7869  *\r
7870  *  Menu enables. Used when setting various modes.\r
7871  *\r
7872 \*---------------------------------------------------------------------------*/\r
7873 \r
7874 typedef struct {\r
7875   int item;\r
7876   int flags;\r
7877 } Enables;\r
7878 \r
7879 VOID\r
7880 GreyRevert(Boolean grey)\r
7881 { // [HGM] vari: for retracting variations in local mode\r
7882   HMENU hmenu = GetMenu(hwndMain);\r
7883   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7884   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7885 }\r
7886 \r
7887 VOID\r
7888 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7889 {\r
7890   while (enab->item > 0) {\r
7891     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7892     enab++;\r
7893   }\r
7894 }\r
7895 \r
7896 Enables gnuEnables[] = {\r
7897   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7898   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7899   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7900   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7901   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7902   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7903   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7904   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7905   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7906   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7907   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7908   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7909   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7910 \r
7911   // Needed to switch from ncp to GNU mode on Engine Load\r
7912   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7913   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7914   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7915   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7916   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7917   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7918   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7919   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7920   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7921   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7922   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7923   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7924   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7925   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7926   { -1, -1 }\r
7927 };\r
7928 \r
7929 Enables icsEnables[] = {\r
7930   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7931   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7932   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7933   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7934   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7935   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7936   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7937   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7938   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7939   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7940   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7941   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7942   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7943   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
7944   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
7945   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7946   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7947   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7948   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7949   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
7950   { -1, -1 }\r
7951 };\r
7952 \r
7953 #if ZIPPY\r
7954 Enables zippyEnables[] = {\r
7955   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7956   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7957   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7958   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7959   { -1, -1 }\r
7960 };\r
7961 #endif\r
7962 \r
7963 Enables ncpEnables[] = {\r
7964   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7965   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7966   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7967   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7968   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7969   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7970   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7971   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7972   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7973   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7974   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7975   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7976   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7977   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7978   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7979   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7980   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7981   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7982   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7983   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7984   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7985   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7986   { -1, -1 }\r
7987 };\r
7988 \r
7989 Enables trainingOnEnables[] = {\r
7990   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7991   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7992   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7993   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7994   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7995   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7996   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7997   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7998   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7999   { -1, -1 }\r
8000 };\r
8001 \r
8002 Enables trainingOffEnables[] = {\r
8003   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8004   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
8005   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8006   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8007   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8008   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8009   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8010   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8011   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8012   { -1, -1 }\r
8013 };\r
8014 \r
8015 /* These modify either ncpEnables or gnuEnables */\r
8016 Enables cmailEnables[] = {\r
8017   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8018   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8019   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8020   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8021   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8022   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8023   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8024   { -1, -1 }\r
8025 };\r
8026 \r
8027 Enables machineThinkingEnables[] = {\r
8028   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8029   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8030   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8031   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8032   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8033   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8034   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8035   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8036   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8037   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8038   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8039   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8040   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8041 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8042   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8043   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8044   { -1, -1 }\r
8045 };\r
8046 \r
8047 Enables userThinkingEnables[] = {\r
8048   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8049   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8050   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8051   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8052   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8053   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8054   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8055   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8056   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8057   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8058   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8059   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8060   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8061 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8062   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8063   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8064   { -1, -1 }\r
8065 };\r
8066 \r
8067 /*---------------------------------------------------------------------------*\\r
8068  *\r
8069  *  Front-end interface functions exported by XBoard.\r
8070  *  Functions appear in same order as prototypes in frontend.h.\r
8071  * \r
8072 \*---------------------------------------------------------------------------*/\r
8073 VOID\r
8074 CheckMark(UINT item, int state)\r
8075 {\r
8076     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
8077 }\r
8078 \r
8079 VOID\r
8080 ModeHighlight()\r
8081 {\r
8082   static UINT prevChecked = 0;\r
8083   static int prevPausing = 0;\r
8084   UINT nowChecked;\r
8085 \r
8086   if (pausing != prevPausing) {\r
8087     prevPausing = pausing;\r
8088     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8089                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8090     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8091   }\r
8092 \r
8093   switch (gameMode) {\r
8094   case BeginningOfGame:\r
8095     if (appData.icsActive)\r
8096       nowChecked = IDM_IcsClient;\r
8097     else if (appData.noChessProgram)\r
8098       nowChecked = IDM_EditGame;\r
8099     else\r
8100       nowChecked = IDM_MachineBlack;\r
8101     break;\r
8102   case MachinePlaysBlack:\r
8103     nowChecked = IDM_MachineBlack;\r
8104     break;\r
8105   case MachinePlaysWhite:\r
8106     nowChecked = IDM_MachineWhite;\r
8107     break;\r
8108   case TwoMachinesPlay:\r
8109     nowChecked = IDM_TwoMachines;\r
8110     break;\r
8111   case AnalyzeMode:\r
8112     nowChecked = IDM_AnalysisMode;\r
8113     break;\r
8114   case AnalyzeFile:\r
8115     nowChecked = IDM_AnalyzeFile;\r
8116     break;\r
8117   case EditGame:\r
8118     nowChecked = IDM_EditGame;\r
8119     break;\r
8120   case PlayFromGameFile:\r
8121     nowChecked = IDM_LoadGame;\r
8122     break;\r
8123   case EditPosition:\r
8124     nowChecked = IDM_EditPosition;\r
8125     break;\r
8126   case Training:\r
8127     nowChecked = IDM_Training;\r
8128     break;\r
8129   case IcsPlayingWhite:\r
8130   case IcsPlayingBlack:\r
8131   case IcsObserving:\r
8132   case IcsIdle:\r
8133     nowChecked = IDM_IcsClient;\r
8134     break;\r
8135   default:\r
8136   case EndOfGame:\r
8137     nowChecked = 0;\r
8138     break;\r
8139   }\r
8140   CheckMark(prevChecked, MF_UNCHECKED);\r
8141   CheckMark(nowChecked, MF_CHECKED);\r
8142   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8143 \r
8144   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8145     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8146                           MF_BYCOMMAND|MF_ENABLED);\r
8147   } else {\r
8148     (void) EnableMenuItem(GetMenu(hwndMain), \r
8149                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8150   }\r
8151 \r
8152   prevChecked = nowChecked;\r
8153 \r
8154   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8155   if (appData.icsActive) {\r
8156        if (appData.icsEngineAnalyze) {\r
8157                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8158        } else {\r
8159                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8160        }\r
8161   }\r
8162   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8163 }\r
8164 \r
8165 VOID\r
8166 SetICSMode()\r
8167 {\r
8168   HMENU hmenu = GetMenu(hwndMain);\r
8169   SetMenuEnables(hmenu, icsEnables);\r
8170   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8171     MF_BYCOMMAND|MF_ENABLED);\r
8172 #if ZIPPY\r
8173   if (appData.zippyPlay) {\r
8174     SetMenuEnables(hmenu, zippyEnables);\r
8175     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8176          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8177           MF_BYCOMMAND|MF_ENABLED);\r
8178   }\r
8179 #endif\r
8180 }\r
8181 \r
8182 VOID\r
8183 SetGNUMode()\r
8184 {\r
8185   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8186 }\r
8187 \r
8188 VOID\r
8189 SetNCPMode()\r
8190 {\r
8191   HMENU hmenu = GetMenu(hwndMain);\r
8192   SetMenuEnables(hmenu, ncpEnables);\r
8193     DrawMenuBar(hwndMain);\r
8194 }\r
8195 \r
8196 VOID\r
8197 SetCmailMode()\r
8198 {\r
8199   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8200 }\r
8201 \r
8202 VOID \r
8203 SetTrainingModeOn()\r
8204 {\r
8205   int i;\r
8206   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8207   for (i = 0; i < N_BUTTONS; i++) {\r
8208     if (buttonDesc[i].hwnd != NULL)\r
8209       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8210   }\r
8211   CommentPopDown();\r
8212 }\r
8213 \r
8214 VOID SetTrainingModeOff()\r
8215 {\r
8216   int i;\r
8217   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8218   for (i = 0; i < N_BUTTONS; i++) {\r
8219     if (buttonDesc[i].hwnd != NULL)\r
8220       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8221   }\r
8222 }\r
8223 \r
8224 \r
8225 VOID\r
8226 SetUserThinkingEnables()\r
8227 {\r
8228   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8229 }\r
8230 \r
8231 VOID\r
8232 SetMachineThinkingEnables()\r
8233 {\r
8234   HMENU hMenu = GetMenu(hwndMain);\r
8235   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8236 \r
8237   SetMenuEnables(hMenu, machineThinkingEnables);\r
8238 \r
8239   if (gameMode == MachinePlaysBlack) {\r
8240     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8241   } else if (gameMode == MachinePlaysWhite) {\r
8242     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8243   } else if (gameMode == TwoMachinesPlay) {\r
8244     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8245   }\r
8246 }\r
8247 \r
8248 \r
8249 VOID\r
8250 DisplayTitle(char *str)\r
8251 {\r
8252   char title[MSG_SIZ], *host;\r
8253   if (str[0] != NULLCHAR) {\r
8254     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8255   } else if (appData.icsActive) {\r
8256     if (appData.icsCommPort[0] != NULLCHAR)\r
8257       host = "ICS";\r
8258     else \r
8259       host = appData.icsHost;\r
8260       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8261   } else if (appData.noChessProgram) {\r
8262     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8263   } else {\r
8264     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8265     strcat(title, ": ");\r
8266     strcat(title, first.tidy);\r
8267   }\r
8268   SetWindowText(hwndMain, title);\r
8269 }\r
8270 \r
8271 \r
8272 VOID\r
8273 DisplayMessage(char *str1, char *str2)\r
8274 {\r
8275   HDC hdc;\r
8276   HFONT oldFont;\r
8277   int remain = MESSAGE_TEXT_MAX - 1;\r
8278   int len;\r
8279 \r
8280   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8281   messageText[0] = NULLCHAR;\r
8282   if (*str1) {\r
8283     len = strlen(str1);\r
8284     if (len > remain) len = remain;\r
8285     strncpy(messageText, str1, len);\r
8286     messageText[len] = NULLCHAR;\r
8287     remain -= len;\r
8288   }\r
8289   if (*str2 && remain >= 2) {\r
8290     if (*str1) {\r
8291       strcat(messageText, "  ");\r
8292       remain -= 2;\r
8293     }\r
8294     len = strlen(str2);\r
8295     if (len > remain) len = remain;\r
8296     strncat(messageText, str2, len);\r
8297   }\r
8298   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8299   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8300 \r
8301   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8302 \r
8303   SAYMACHINEMOVE();\r
8304 \r
8305   hdc = GetDC(hwndMain);\r
8306   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8307   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8308              &messageRect, messageText, strlen(messageText), NULL);\r
8309   (void) SelectObject(hdc, oldFont);\r
8310   (void) ReleaseDC(hwndMain, hdc);\r
8311 }\r
8312 \r
8313 VOID\r
8314 DisplayError(char *str, int error)\r
8315 {\r
8316   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8317   int len;\r
8318 \r
8319   if (error == 0) {\r
8320     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8321   } else {\r
8322     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8323                         NULL, error, LANG_NEUTRAL,\r
8324                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8325     if (len > 0) {\r
8326       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8327     } else {\r
8328       ErrorMap *em = errmap;\r
8329       while (em->err != 0 && em->err != error) em++;\r
8330       if (em->err != 0) {\r
8331         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8332       } else {\r
8333         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8334       }\r
8335     }\r
8336   }\r
8337   \r
8338   ErrorPopUp(_("Error"), buf);\r
8339 }\r
8340 \r
8341 \r
8342 VOID\r
8343 DisplayMoveError(char *str)\r
8344 {\r
8345   fromX = fromY = -1;\r
8346   ClearHighlights();\r
8347   DrawPosition(FALSE, NULL);\r
8348   if (appData.popupMoveErrors) {\r
8349     ErrorPopUp(_("Error"), str);\r
8350   } else {\r
8351     DisplayMessage(str, "");\r
8352     moveErrorMessageUp = TRUE;\r
8353   }\r
8354 }\r
8355 \r
8356 VOID\r
8357 DisplayFatalError(char *str, int error, int exitStatus)\r
8358 {\r
8359   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8360   int len;\r
8361   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8362 \r
8363   if (error != 0) {\r
8364     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8365                         NULL, error, LANG_NEUTRAL,\r
8366                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8367     if (len > 0) {\r
8368       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8369     } else {\r
8370       ErrorMap *em = errmap;\r
8371       while (em->err != 0 && em->err != error) em++;\r
8372       if (em->err != 0) {\r
8373         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8374       } else {\r
8375         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8376       }\r
8377     }\r
8378     str = buf;\r
8379   }\r
8380   if (appData.debugMode) {\r
8381     fprintf(debugFP, "%s: %s\n", label, str);\r
8382   }\r
8383   if (appData.popupExitMessage) {\r
8384     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8385                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8386   }\r
8387   ExitEvent(exitStatus);\r
8388 }\r
8389 \r
8390 \r
8391 VOID\r
8392 DisplayInformation(char *str)\r
8393 {\r
8394   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8395 }\r
8396 \r
8397 \r
8398 VOID\r
8399 DisplayNote(char *str)\r
8400 {\r
8401   ErrorPopUp(_("Note"), str);\r
8402 }\r
8403 \r
8404 \r
8405 typedef struct {\r
8406   char *title, *question, *replyPrefix;\r
8407   ProcRef pr;\r
8408 } QuestionParams;\r
8409 \r
8410 LRESULT CALLBACK\r
8411 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8412 {\r
8413   static QuestionParams *qp;\r
8414   char reply[MSG_SIZ];\r
8415   int len, err;\r
8416 \r
8417   switch (message) {\r
8418   case WM_INITDIALOG:\r
8419     qp = (QuestionParams *) lParam;\r
8420     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8421     Translate(hDlg, DLG_Question);\r
8422     SetWindowText(hDlg, qp->title);\r
8423     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8424     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8425     return FALSE;\r
8426 \r
8427   case WM_COMMAND:\r
8428     switch (LOWORD(wParam)) {\r
8429     case IDOK:\r
8430       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8431       if (*reply) strcat(reply, " ");\r
8432       len = strlen(reply);\r
8433       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8434       strcat(reply, "\n");\r
8435       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8436       EndDialog(hDlg, TRUE);\r
8437       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8438       return TRUE;\r
8439     case IDCANCEL:\r
8440       EndDialog(hDlg, FALSE);\r
8441       return TRUE;\r
8442     default:\r
8443       break;\r
8444     }\r
8445     break;\r
8446   }\r
8447   return FALSE;\r
8448 }\r
8449 \r
8450 VOID\r
8451 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8452 {\r
8453     QuestionParams qp;\r
8454     FARPROC lpProc;\r
8455     \r
8456     qp.title = title;\r
8457     qp.question = question;\r
8458     qp.replyPrefix = replyPrefix;\r
8459     qp.pr = pr;\r
8460     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8461     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8462       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8463     FreeProcInstance(lpProc);\r
8464 }\r
8465 \r
8466 /* [AS] Pick FRC position */\r
8467 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8468 {\r
8469     static int * lpIndexFRC;\r
8470     BOOL index_is_ok;\r
8471     char buf[16];\r
8472 \r
8473     switch( message )\r
8474     {\r
8475     case WM_INITDIALOG:\r
8476         lpIndexFRC = (int *) lParam;\r
8477 \r
8478         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8479         Translate(hDlg, DLG_NewGameFRC);\r
8480 \r
8481         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8482         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8483         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8484         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8485 \r
8486         break;\r
8487 \r
8488     case WM_COMMAND:\r
8489         switch( LOWORD(wParam) ) {\r
8490         case IDOK:\r
8491             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8492             EndDialog( hDlg, 0 );\r
8493             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8494             return TRUE;\r
8495         case IDCANCEL:\r
8496             EndDialog( hDlg, 1 );   \r
8497             return TRUE;\r
8498         case IDC_NFG_Edit:\r
8499             if( HIWORD(wParam) == EN_CHANGE ) {\r
8500                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8501 \r
8502                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8503             }\r
8504             return TRUE;\r
8505         case IDC_NFG_Random:\r
8506           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8507             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8508             return TRUE;\r
8509         }\r
8510 \r
8511         break;\r
8512     }\r
8513 \r
8514     return FALSE;\r
8515 }\r
8516 \r
8517 int NewGameFRC()\r
8518 {\r
8519     int result;\r
8520     int index = appData.defaultFrcPosition;\r
8521     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8522 \r
8523     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8524 \r
8525     if( result == 0 ) {\r
8526         appData.defaultFrcPosition = index;\r
8527     }\r
8528 \r
8529     return result;\r
8530 }\r
8531 \r
8532 /* [AS] Game list options. Refactored by HGM */\r
8533 \r
8534 HWND gameListOptionsDialog;\r
8535 \r
8536 // low-level front-end: clear text edit / list widget\r
8537 void\r
8538 GLT_ClearList()\r
8539 {\r
8540     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8541 }\r
8542 \r
8543 // low-level front-end: clear text edit / list widget\r
8544 void\r
8545 GLT_DeSelectList()\r
8546 {\r
8547     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8548 }\r
8549 \r
8550 // low-level front-end: append line to text edit / list widget\r
8551 void\r
8552 GLT_AddToList( char *name )\r
8553 {\r
8554     if( name != 0 ) {\r
8555             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8556     }\r
8557 }\r
8558 \r
8559 // low-level front-end: get line from text edit / list widget\r
8560 Boolean\r
8561 GLT_GetFromList( int index, char *name )\r
8562 {\r
8563     if( name != 0 ) {\r
8564             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8565                 return TRUE;\r
8566     }\r
8567     return FALSE;\r
8568 }\r
8569 \r
8570 void GLT_MoveSelection( HWND hDlg, int delta )\r
8571 {\r
8572     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8573     int idx2 = idx1 + delta;\r
8574     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8575 \r
8576     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8577         char buf[128];\r
8578 \r
8579         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8580         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8581         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8582         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8583     }\r
8584 }\r
8585 \r
8586 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8587 {\r
8588     switch( message )\r
8589     {\r
8590     case WM_INITDIALOG:\r
8591         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8592         \r
8593         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8594         Translate(hDlg, DLG_GameListOptions);\r
8595 \r
8596         /* Initialize list */\r
8597         GLT_TagsToList( lpUserGLT );\r
8598 \r
8599         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8600 \r
8601         break;\r
8602 \r
8603     case WM_COMMAND:\r
8604         switch( LOWORD(wParam) ) {\r
8605         case IDOK:\r
8606             GLT_ParseList();\r
8607             EndDialog( hDlg, 0 );\r
8608             return TRUE;\r
8609         case IDCANCEL:\r
8610             EndDialog( hDlg, 1 );\r
8611             return TRUE;\r
8612 \r
8613         case IDC_GLT_Default:\r
8614             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8615             return TRUE;\r
8616 \r
8617         case IDC_GLT_Restore:\r
8618             GLT_TagsToList( appData.gameListTags );\r
8619             return TRUE;\r
8620 \r
8621         case IDC_GLT_Up:\r
8622             GLT_MoveSelection( hDlg, -1 );\r
8623             return TRUE;\r
8624 \r
8625         case IDC_GLT_Down:\r
8626             GLT_MoveSelection( hDlg, +1 );\r
8627             return TRUE;\r
8628         }\r
8629 \r
8630         break;\r
8631     }\r
8632 \r
8633     return FALSE;\r
8634 }\r
8635 \r
8636 int GameListOptions()\r
8637 {\r
8638     int result;\r
8639     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8640 \r
8641       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8642 \r
8643     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8644 \r
8645     if( result == 0 ) {\r
8646         /* [AS] Memory leak here! */\r
8647         appData.gameListTags = strdup( lpUserGLT ); \r
8648     }\r
8649 \r
8650     return result;\r
8651 }\r
8652 \r
8653 VOID\r
8654 DisplayIcsInteractionTitle(char *str)\r
8655 {\r
8656   char consoleTitle[MSG_SIZ];\r
8657 \r
8658     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8659     SetWindowText(hwndConsole, consoleTitle);\r
8660 \r
8661     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8662       char buf[MSG_SIZ], *p = buf, *q;\r
8663         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8664       do {\r
8665         q = strchr(p, ';');\r
8666         if(q) *q++ = 0;\r
8667         if(*p) ChatPopUp(p);\r
8668       } while(p=q);\r
8669     }\r
8670 \r
8671     SetActiveWindow(hwndMain);\r
8672 }\r
8673 \r
8674 void\r
8675 DrawPosition(int fullRedraw, Board board)\r
8676 {\r
8677   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8678 }\r
8679 \r
8680 void NotifyFrontendLogin()\r
8681 {\r
8682         if (hwndConsole)\r
8683                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8684 }\r
8685 \r
8686 VOID\r
8687 ResetFrontEnd()\r
8688 {\r
8689   fromX = fromY = -1;\r
8690   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8691     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8692     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8693     dragInfo.lastpos = dragInfo.pos;\r
8694     dragInfo.start.x = dragInfo.start.y = -1;\r
8695     dragInfo.from = dragInfo.start;\r
8696     ReleaseCapture();\r
8697     DrawPosition(TRUE, NULL);\r
8698   }\r
8699   TagsPopDown();\r
8700 }\r
8701 \r
8702 \r
8703 VOID\r
8704 CommentPopUp(char *title, char *str)\r
8705 {\r
8706   HWND hwnd = GetActiveWindow();\r
8707   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8708   SAY(str);\r
8709   SetActiveWindow(hwnd);\r
8710 }\r
8711 \r
8712 VOID\r
8713 CommentPopDown(void)\r
8714 {\r
8715   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8716   if (commentDialog) {\r
8717     ShowWindow(commentDialog, SW_HIDE);\r
8718   }\r
8719   commentUp = FALSE;\r
8720 }\r
8721 \r
8722 VOID\r
8723 EditCommentPopUp(int index, char *title, char *str)\r
8724 {\r
8725   EitherCommentPopUp(index, title, str, TRUE);\r
8726 }\r
8727 \r
8728 \r
8729 int\r
8730 Roar()\r
8731 {\r
8732   MyPlaySound(&sounds[(int)SoundRoar]);\r
8733   return 1;\r
8734 }\r
8735 \r
8736 VOID\r
8737 RingBell()\r
8738 {\r
8739   MyPlaySound(&sounds[(int)SoundMove]);\r
8740 }\r
8741 \r
8742 VOID PlayIcsWinSound()\r
8743 {\r
8744   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8745 }\r
8746 \r
8747 VOID PlayIcsLossSound()\r
8748 {\r
8749   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8750 }\r
8751 \r
8752 VOID PlayIcsDrawSound()\r
8753 {\r
8754   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8755 }\r
8756 \r
8757 VOID PlayIcsUnfinishedSound()\r
8758 {\r
8759   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8760 }\r
8761 \r
8762 VOID\r
8763 PlayAlarmSound()\r
8764 {\r
8765   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8766 }\r
8767 \r
8768 VOID\r
8769 PlayTellSound()\r
8770 {\r
8771   MyPlaySound(&textAttribs[ColorTell].sound);\r
8772 }\r
8773 \r
8774 \r
8775 VOID\r
8776 EchoOn()\r
8777 {\r
8778   HWND hInput;\r
8779   consoleEcho = TRUE;\r
8780   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8781   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8782   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8783 }\r
8784 \r
8785 \r
8786 VOID\r
8787 EchoOff()\r
8788 {\r
8789   CHARFORMAT cf;\r
8790   HWND hInput;\r
8791   consoleEcho = FALSE;\r
8792   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8793   /* This works OK: set text and background both to the same color */\r
8794   cf = consoleCF;\r
8795   cf.crTextColor = COLOR_ECHOOFF;\r
8796   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8797   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8798 }\r
8799 \r
8800 /* No Raw()...? */\r
8801 \r
8802 void Colorize(ColorClass cc, int continuation)\r
8803 {\r
8804   currentColorClass = cc;\r
8805   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8806   consoleCF.crTextColor = textAttribs[cc].color;\r
8807   consoleCF.dwEffects = textAttribs[cc].effects;\r
8808   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8809 }\r
8810 \r
8811 char *\r
8812 UserName()\r
8813 {\r
8814   static char buf[MSG_SIZ];\r
8815   DWORD bufsiz = MSG_SIZ;\r
8816 \r
8817   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8818         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8819   }\r
8820   if (!GetUserName(buf, &bufsiz)) {\r
8821     /*DisplayError("Error getting user name", GetLastError());*/\r
8822     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8823   }\r
8824   return buf;\r
8825 }\r
8826 \r
8827 char *\r
8828 HostName()\r
8829 {\r
8830   static char buf[MSG_SIZ];\r
8831   DWORD bufsiz = MSG_SIZ;\r
8832 \r
8833   if (!GetComputerName(buf, &bufsiz)) {\r
8834     /*DisplayError("Error getting host name", GetLastError());*/\r
8835     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8836   }\r
8837   return buf;\r
8838 }\r
8839 \r
8840 \r
8841 int\r
8842 ClockTimerRunning()\r
8843 {\r
8844   return clockTimerEvent != 0;\r
8845 }\r
8846 \r
8847 int\r
8848 StopClockTimer()\r
8849 {\r
8850   if (clockTimerEvent == 0) return FALSE;\r
8851   KillTimer(hwndMain, clockTimerEvent);\r
8852   clockTimerEvent = 0;\r
8853   return TRUE;\r
8854 }\r
8855 \r
8856 void\r
8857 StartClockTimer(long millisec)\r
8858 {\r
8859   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8860                              (UINT) millisec, NULL);\r
8861 }\r
8862 \r
8863 void\r
8864 DisplayWhiteClock(long timeRemaining, int highlight)\r
8865 {\r
8866   HDC hdc;\r
8867   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8868 \r
8869   if(appData.noGUI) return;\r
8870   hdc = GetDC(hwndMain);\r
8871   if (!IsIconic(hwndMain)) {\r
8872     DisplayAClock(hdc, timeRemaining, highlight, \r
8873                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8874   }\r
8875   if (highlight && iconCurrent == iconBlack) {\r
8876     iconCurrent = iconWhite;\r
8877     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8878     if (IsIconic(hwndMain)) {\r
8879       DrawIcon(hdc, 2, 2, iconCurrent);\r
8880     }\r
8881   }\r
8882   (void) ReleaseDC(hwndMain, hdc);\r
8883   if (hwndConsole)\r
8884     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8885 }\r
8886 \r
8887 void\r
8888 DisplayBlackClock(long timeRemaining, int highlight)\r
8889 {\r
8890   HDC hdc;\r
8891   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8892 \r
8893 \r
8894   if(appData.noGUI) return;\r
8895   hdc = GetDC(hwndMain);\r
8896   if (!IsIconic(hwndMain)) {\r
8897     DisplayAClock(hdc, timeRemaining, highlight, \r
8898                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8899   }\r
8900   if (highlight && iconCurrent == iconWhite) {\r
8901     iconCurrent = iconBlack;\r
8902     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8903     if (IsIconic(hwndMain)) {\r
8904       DrawIcon(hdc, 2, 2, iconCurrent);\r
8905     }\r
8906   }\r
8907   (void) ReleaseDC(hwndMain, hdc);\r
8908   if (hwndConsole)\r
8909     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8910 }\r
8911 \r
8912 \r
8913 int\r
8914 LoadGameTimerRunning()\r
8915 {\r
8916   return loadGameTimerEvent != 0;\r
8917 }\r
8918 \r
8919 int\r
8920 StopLoadGameTimer()\r
8921 {\r
8922   if (loadGameTimerEvent == 0) return FALSE;\r
8923   KillTimer(hwndMain, loadGameTimerEvent);\r
8924   loadGameTimerEvent = 0;\r
8925   return TRUE;\r
8926 }\r
8927 \r
8928 void\r
8929 StartLoadGameTimer(long millisec)\r
8930 {\r
8931   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8932                                 (UINT) millisec, NULL);\r
8933 }\r
8934 \r
8935 void\r
8936 AutoSaveGame()\r
8937 {\r
8938   char *defName;\r
8939   FILE *f;\r
8940   char fileTitle[MSG_SIZ];\r
8941 \r
8942   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8943   f = OpenFileDialog(hwndMain, "a", defName,\r
8944                      appData.oldSaveStyle ? "gam" : "pgn",\r
8945                      GAME_FILT, \r
8946                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8947   if (f != NULL) {\r
8948     SaveGame(f, 0, "");\r
8949     fclose(f);\r
8950   }\r
8951 }\r
8952 \r
8953 \r
8954 void\r
8955 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8956 {\r
8957   if (delayedTimerEvent != 0) {\r
8958     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8959       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8960     }\r
8961     KillTimer(hwndMain, delayedTimerEvent);\r
8962     delayedTimerEvent = 0;\r
8963     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8964     delayedTimerCallback();\r
8965   }\r
8966   delayedTimerCallback = cb;\r
8967   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8968                                 (UINT) millisec, NULL);\r
8969 }\r
8970 \r
8971 DelayedEventCallback\r
8972 GetDelayedEvent()\r
8973 {\r
8974   if (delayedTimerEvent) {\r
8975     return delayedTimerCallback;\r
8976   } else {\r
8977     return NULL;\r
8978   }\r
8979 }\r
8980 \r
8981 void\r
8982 CancelDelayedEvent()\r
8983 {\r
8984   if (delayedTimerEvent) {\r
8985     KillTimer(hwndMain, delayedTimerEvent);\r
8986     delayedTimerEvent = 0;\r
8987   }\r
8988 }\r
8989 \r
8990 DWORD GetWin32Priority(int nice)\r
8991 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8992 /*\r
8993 REALTIME_PRIORITY_CLASS     0x00000100\r
8994 HIGH_PRIORITY_CLASS         0x00000080\r
8995 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8996 NORMAL_PRIORITY_CLASS       0x00000020\r
8997 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8998 IDLE_PRIORITY_CLASS         0x00000040\r
8999 */\r
9000         if (nice < -15) return 0x00000080;\r
9001         if (nice < 0)   return 0x00008000;\r
9002         if (nice == 0)  return 0x00000020;\r
9003         if (nice < 15)  return 0x00004000;\r
9004         return 0x00000040;\r
9005 }\r
9006 \r
9007 void RunCommand(char *cmdLine)\r
9008 {\r
9009   /* Now create the child process. */\r
9010   STARTUPINFO siStartInfo;\r
9011   PROCESS_INFORMATION piProcInfo;\r
9012 \r
9013   siStartInfo.cb = sizeof(STARTUPINFO);\r
9014   siStartInfo.lpReserved = NULL;\r
9015   siStartInfo.lpDesktop = NULL;\r
9016   siStartInfo.lpTitle = NULL;\r
9017   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9018   siStartInfo.cbReserved2 = 0;\r
9019   siStartInfo.lpReserved2 = NULL;\r
9020   siStartInfo.hStdInput = NULL;\r
9021   siStartInfo.hStdOutput = NULL;\r
9022   siStartInfo.hStdError = NULL;\r
9023 \r
9024   CreateProcess(NULL,\r
9025                 cmdLine,           /* command line */\r
9026                 NULL,      /* process security attributes */\r
9027                 NULL,      /* primary thread security attrs */\r
9028                 TRUE,      /* handles are inherited */\r
9029                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9030                 NULL,      /* use parent's environment */\r
9031                 NULL,\r
9032                 &siStartInfo, /* STARTUPINFO pointer */\r
9033                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
9034 \r
9035   CloseHandle(piProcInfo.hThread);\r
9036 }\r
9037 \r
9038 /* Start a child process running the given program.\r
9039    The process's standard output can be read from "from", and its\r
9040    standard input can be written to "to".\r
9041    Exit with fatal error if anything goes wrong.\r
9042    Returns an opaque pointer that can be used to destroy the process\r
9043    later.\r
9044 */\r
9045 int\r
9046 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9047 {\r
9048 #define BUFSIZE 4096\r
9049 \r
9050   HANDLE hChildStdinRd, hChildStdinWr,\r
9051     hChildStdoutRd, hChildStdoutWr;\r
9052   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9053   SECURITY_ATTRIBUTES saAttr;\r
9054   BOOL fSuccess;\r
9055   PROCESS_INFORMATION piProcInfo;\r
9056   STARTUPINFO siStartInfo;\r
9057   ChildProc *cp;\r
9058   char buf[MSG_SIZ];\r
9059   DWORD err;\r
9060 \r
9061   if (appData.debugMode) {\r
9062     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9063   }\r
9064 \r
9065   *pr = NoProc;\r
9066 \r
9067   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9068   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9069   saAttr.bInheritHandle = TRUE;\r
9070   saAttr.lpSecurityDescriptor = NULL;\r
9071 \r
9072   /*\r
9073    * The steps for redirecting child's STDOUT:\r
9074    *     1. Create anonymous pipe to be STDOUT for child.\r
9075    *     2. Create a noninheritable duplicate of read handle,\r
9076    *         and close the inheritable read handle.\r
9077    */\r
9078 \r
9079   /* Create a pipe for the child's STDOUT. */\r
9080   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9081     return GetLastError();\r
9082   }\r
9083 \r
9084   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9085   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9086                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9087                              FALSE,     /* not inherited */\r
9088                              DUPLICATE_SAME_ACCESS);\r
9089   if (! fSuccess) {\r
9090     return GetLastError();\r
9091   }\r
9092   CloseHandle(hChildStdoutRd);\r
9093 \r
9094   /*\r
9095    * The steps for redirecting child's STDIN:\r
9096    *     1. Create anonymous pipe to be STDIN for child.\r
9097    *     2. Create a noninheritable duplicate of write handle,\r
9098    *         and close the inheritable write handle.\r
9099    */\r
9100 \r
9101   /* Create a pipe for the child's STDIN. */\r
9102   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9103     return GetLastError();\r
9104   }\r
9105 \r
9106   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9107   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9108                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9109                              FALSE,     /* not inherited */\r
9110                              DUPLICATE_SAME_ACCESS);\r
9111   if (! fSuccess) {\r
9112     return GetLastError();\r
9113   }\r
9114   CloseHandle(hChildStdinWr);\r
9115 \r
9116   /* Arrange to (1) look in dir for the child .exe file, and\r
9117    * (2) have dir be the child's working directory.  Interpret\r
9118    * dir relative to the directory WinBoard loaded from. */\r
9119   GetCurrentDirectory(MSG_SIZ, buf);\r
9120   SetCurrentDirectory(installDir);\r
9121   SetCurrentDirectory(dir);\r
9122 \r
9123   /* Now create the child process. */\r
9124 \r
9125   siStartInfo.cb = sizeof(STARTUPINFO);\r
9126   siStartInfo.lpReserved = NULL;\r
9127   siStartInfo.lpDesktop = NULL;\r
9128   siStartInfo.lpTitle = NULL;\r
9129   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9130   siStartInfo.cbReserved2 = 0;\r
9131   siStartInfo.lpReserved2 = NULL;\r
9132   siStartInfo.hStdInput = hChildStdinRd;\r
9133   siStartInfo.hStdOutput = hChildStdoutWr;\r
9134   siStartInfo.hStdError = hChildStdoutWr;\r
9135 \r
9136   fSuccess = CreateProcess(NULL,\r
9137                            cmdLine,        /* command line */\r
9138                            NULL,           /* process security attributes */\r
9139                            NULL,           /* primary thread security attrs */\r
9140                            TRUE,           /* handles are inherited */\r
9141                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9142                            NULL,           /* use parent's environment */\r
9143                            NULL,\r
9144                            &siStartInfo, /* STARTUPINFO pointer */\r
9145                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9146 \r
9147   err = GetLastError();\r
9148   SetCurrentDirectory(buf); /* return to prev directory */\r
9149   if (! fSuccess) {\r
9150     return err;\r
9151   }\r
9152 \r
9153   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9154     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9155     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9156   }\r
9157 \r
9158   /* Close the handles we don't need in the parent */\r
9159   CloseHandle(piProcInfo.hThread);\r
9160   CloseHandle(hChildStdinRd);\r
9161   CloseHandle(hChildStdoutWr);\r
9162 \r
9163   /* Prepare return value */\r
9164   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9165   cp->kind = CPReal;\r
9166   cp->hProcess = piProcInfo.hProcess;\r
9167   cp->pid = piProcInfo.dwProcessId;\r
9168   cp->hFrom = hChildStdoutRdDup;\r
9169   cp->hTo = hChildStdinWrDup;\r
9170 \r
9171   *pr = (void *) cp;\r
9172 \r
9173   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9174      2000 where engines sometimes don't see the initial command(s)\r
9175      from WinBoard and hang.  I don't understand how that can happen,\r
9176      but the Sleep is harmless, so I've put it in.  Others have also\r
9177      reported what may be the same problem, so hopefully this will fix\r
9178      it for them too.  */\r
9179   Sleep(500);\r
9180 \r
9181   return NO_ERROR;\r
9182 }\r
9183 \r
9184 \r
9185 void\r
9186 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9187 {\r
9188   ChildProc *cp; int result;\r
9189 \r
9190   cp = (ChildProc *) pr;\r
9191   if (cp == NULL) return;\r
9192 \r
9193   switch (cp->kind) {\r
9194   case CPReal:\r
9195     /* TerminateProcess is considered harmful, so... */\r
9196     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9197     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9198     /* The following doesn't work because the chess program\r
9199        doesn't "have the same console" as WinBoard.  Maybe\r
9200        we could arrange for this even though neither WinBoard\r
9201        nor the chess program uses a console for stdio? */\r
9202     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9203 \r
9204     /* [AS] Special termination modes for misbehaving programs... */\r
9205     if( signal == 9 ) { \r
9206         result = TerminateProcess( cp->hProcess, 0 );\r
9207 \r
9208         if ( appData.debugMode) {\r
9209             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9210         }\r
9211     }\r
9212     else if( signal == 10 ) {\r
9213         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9214 \r
9215         if( dw != WAIT_OBJECT_0 ) {\r
9216             result = TerminateProcess( cp->hProcess, 0 );\r
9217 \r
9218             if ( appData.debugMode) {\r
9219                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9220             }\r
9221 \r
9222         }\r
9223     }\r
9224 \r
9225     CloseHandle(cp->hProcess);\r
9226     break;\r
9227 \r
9228   case CPComm:\r
9229     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9230     break;\r
9231 \r
9232   case CPSock:\r
9233     closesocket(cp->sock);\r
9234     WSACleanup();\r
9235     break;\r
9236 \r
9237   case CPRcmd:\r
9238     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9239     closesocket(cp->sock);\r
9240     closesocket(cp->sock2);\r
9241     WSACleanup();\r
9242     break;\r
9243   }\r
9244   free(cp);\r
9245 }\r
9246 \r
9247 void\r
9248 InterruptChildProcess(ProcRef pr)\r
9249 {\r
9250   ChildProc *cp;\r
9251 \r
9252   cp = (ChildProc *) pr;\r
9253   if (cp == NULL) return;\r
9254   switch (cp->kind) {\r
9255   case CPReal:\r
9256     /* The following doesn't work because the chess program\r
9257        doesn't "have the same console" as WinBoard.  Maybe\r
9258        we could arrange for this even though neither WinBoard\r
9259        nor the chess program uses a console for stdio */\r
9260     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9261     break;\r
9262 \r
9263   case CPComm:\r
9264   case CPSock:\r
9265     /* Can't interrupt */\r
9266     break;\r
9267 \r
9268   case CPRcmd:\r
9269     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9270     break;\r
9271   }\r
9272 }\r
9273 \r
9274 \r
9275 int\r
9276 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9277 {\r
9278   char cmdLine[MSG_SIZ];\r
9279 \r
9280   if (port[0] == NULLCHAR) {\r
9281     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9282   } else {\r
9283     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9284   }\r
9285   return StartChildProcess(cmdLine, "", pr);\r
9286 }\r
9287 \r
9288 \r
9289 /* Code to open TCP sockets */\r
9290 \r
9291 int\r
9292 OpenTCP(char *host, char *port, ProcRef *pr)\r
9293 {\r
9294   ChildProc *cp;\r
9295   int err;\r
9296   SOCKET s;\r
9297 \r
9298   struct sockaddr_in sa, mysa;\r
9299   struct hostent FAR *hp;\r
9300   unsigned short uport;\r
9301   WORD wVersionRequested;\r
9302   WSADATA wsaData;\r
9303 \r
9304   /* Initialize socket DLL */\r
9305   wVersionRequested = MAKEWORD(1, 1);\r
9306   err = WSAStartup(wVersionRequested, &wsaData);\r
9307   if (err != 0) return err;\r
9308 \r
9309   /* Make socket */\r
9310   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9311     err = WSAGetLastError();\r
9312     WSACleanup();\r
9313     return err;\r
9314   }\r
9315 \r
9316   /* Bind local address using (mostly) don't-care values.\r
9317    */\r
9318   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9319   mysa.sin_family = AF_INET;\r
9320   mysa.sin_addr.s_addr = INADDR_ANY;\r
9321   uport = (unsigned short) 0;\r
9322   mysa.sin_port = htons(uport);\r
9323   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9324       == SOCKET_ERROR) {\r
9325     err = WSAGetLastError();\r
9326     WSACleanup();\r
9327     return err;\r
9328   }\r
9329 \r
9330   /* Resolve remote host name */\r
9331   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9332   if (!(hp = gethostbyname(host))) {\r
9333     unsigned int b0, b1, b2, b3;\r
9334 \r
9335     err = WSAGetLastError();\r
9336 \r
9337     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9338       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9339       hp->h_addrtype = AF_INET;\r
9340       hp->h_length = 4;\r
9341       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9342       hp->h_addr_list[0] = (char *) malloc(4);\r
9343       hp->h_addr_list[0][0] = (char) b0;\r
9344       hp->h_addr_list[0][1] = (char) b1;\r
9345       hp->h_addr_list[0][2] = (char) b2;\r
9346       hp->h_addr_list[0][3] = (char) b3;\r
9347     } else {\r
9348       WSACleanup();\r
9349       return err;\r
9350     }\r
9351   }\r
9352   sa.sin_family = hp->h_addrtype;\r
9353   uport = (unsigned short) atoi(port);\r
9354   sa.sin_port = htons(uport);\r
9355   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9356 \r
9357   /* Make connection */\r
9358   if (connect(s, (struct sockaddr *) &sa,\r
9359               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9360     err = WSAGetLastError();\r
9361     WSACleanup();\r
9362     return err;\r
9363   }\r
9364 \r
9365   /* Prepare return value */\r
9366   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9367   cp->kind = CPSock;\r
9368   cp->sock = s;\r
9369   *pr = (ProcRef *) cp;\r
9370 \r
9371   return NO_ERROR;\r
9372 }\r
9373 \r
9374 int\r
9375 OpenCommPort(char *name, ProcRef *pr)\r
9376 {\r
9377   HANDLE h;\r
9378   COMMTIMEOUTS ct;\r
9379   ChildProc *cp;\r
9380   char fullname[MSG_SIZ];\r
9381 \r
9382   if (*name != '\\')\r
9383     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9384   else\r
9385     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9386 \r
9387   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9388                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9389   if (h == (HANDLE) -1) {\r
9390     return GetLastError();\r
9391   }\r
9392   hCommPort = h;\r
9393 \r
9394   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9395 \r
9396   /* Accumulate characters until a 100ms pause, then parse */\r
9397   ct.ReadIntervalTimeout = 100;\r
9398   ct.ReadTotalTimeoutMultiplier = 0;\r
9399   ct.ReadTotalTimeoutConstant = 0;\r
9400   ct.WriteTotalTimeoutMultiplier = 0;\r
9401   ct.WriteTotalTimeoutConstant = 0;\r
9402   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9403 \r
9404   /* Prepare return value */\r
9405   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9406   cp->kind = CPComm;\r
9407   cp->hFrom = h;\r
9408   cp->hTo = h;\r
9409   *pr = (ProcRef *) cp;\r
9410 \r
9411   return NO_ERROR;\r
9412 }\r
9413 \r
9414 int\r
9415 OpenLoopback(ProcRef *pr)\r
9416 {\r
9417   DisplayFatalError(_("Not implemented"), 0, 1);\r
9418   return NO_ERROR;\r
9419 }\r
9420 \r
9421 \r
9422 int\r
9423 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9424 {\r
9425   ChildProc *cp;\r
9426   int err;\r
9427   SOCKET s, s2, s3;\r
9428   struct sockaddr_in sa, mysa;\r
9429   struct hostent FAR *hp;\r
9430   unsigned short uport;\r
9431   WORD wVersionRequested;\r
9432   WSADATA wsaData;\r
9433   int fromPort;\r
9434   char stderrPortStr[MSG_SIZ];\r
9435 \r
9436   /* Initialize socket DLL */\r
9437   wVersionRequested = MAKEWORD(1, 1);\r
9438   err = WSAStartup(wVersionRequested, &wsaData);\r
9439   if (err != 0) return err;\r
9440 \r
9441   /* Resolve remote host name */\r
9442   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9443   if (!(hp = gethostbyname(host))) {\r
9444     unsigned int b0, b1, b2, b3;\r
9445 \r
9446     err = WSAGetLastError();\r
9447 \r
9448     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9449       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9450       hp->h_addrtype = AF_INET;\r
9451       hp->h_length = 4;\r
9452       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9453       hp->h_addr_list[0] = (char *) malloc(4);\r
9454       hp->h_addr_list[0][0] = (char) b0;\r
9455       hp->h_addr_list[0][1] = (char) b1;\r
9456       hp->h_addr_list[0][2] = (char) b2;\r
9457       hp->h_addr_list[0][3] = (char) b3;\r
9458     } else {\r
9459       WSACleanup();\r
9460       return err;\r
9461     }\r
9462   }\r
9463   sa.sin_family = hp->h_addrtype;\r
9464   uport = (unsigned short) 514;\r
9465   sa.sin_port = htons(uport);\r
9466   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9467 \r
9468   /* Bind local socket to unused "privileged" port address\r
9469    */\r
9470   s = INVALID_SOCKET;\r
9471   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9472   mysa.sin_family = AF_INET;\r
9473   mysa.sin_addr.s_addr = INADDR_ANY;\r
9474   for (fromPort = 1023;; fromPort--) {\r
9475     if (fromPort < 0) {\r
9476       WSACleanup();\r
9477       return WSAEADDRINUSE;\r
9478     }\r
9479     if (s == INVALID_SOCKET) {\r
9480       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9481         err = WSAGetLastError();\r
9482         WSACleanup();\r
9483         return err;\r
9484       }\r
9485     }\r
9486     uport = (unsigned short) fromPort;\r
9487     mysa.sin_port = htons(uport);\r
9488     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9489         == SOCKET_ERROR) {\r
9490       err = WSAGetLastError();\r
9491       if (err == WSAEADDRINUSE) continue;\r
9492       WSACleanup();\r
9493       return err;\r
9494     }\r
9495     if (connect(s, (struct sockaddr *) &sa,\r
9496       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9497       err = WSAGetLastError();\r
9498       if (err == WSAEADDRINUSE) {\r
9499         closesocket(s);\r
9500         s = -1;\r
9501         continue;\r
9502       }\r
9503       WSACleanup();\r
9504       return err;\r
9505     }\r
9506     break;\r
9507   }\r
9508 \r
9509   /* Bind stderr local socket to unused "privileged" port address\r
9510    */\r
9511   s2 = INVALID_SOCKET;\r
9512   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9513   mysa.sin_family = AF_INET;\r
9514   mysa.sin_addr.s_addr = INADDR_ANY;\r
9515   for (fromPort = 1023;; fromPort--) {\r
9516     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9517     if (fromPort < 0) {\r
9518       (void) closesocket(s);\r
9519       WSACleanup();\r
9520       return WSAEADDRINUSE;\r
9521     }\r
9522     if (s2 == INVALID_SOCKET) {\r
9523       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9524         err = WSAGetLastError();\r
9525         closesocket(s);\r
9526         WSACleanup();\r
9527         return err;\r
9528       }\r
9529     }\r
9530     uport = (unsigned short) fromPort;\r
9531     mysa.sin_port = htons(uport);\r
9532     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9533         == SOCKET_ERROR) {\r
9534       err = WSAGetLastError();\r
9535       if (err == WSAEADDRINUSE) continue;\r
9536       (void) closesocket(s);\r
9537       WSACleanup();\r
9538       return err;\r
9539     }\r
9540     if (listen(s2, 1) == SOCKET_ERROR) {\r
9541       err = WSAGetLastError();\r
9542       if (err == WSAEADDRINUSE) {\r
9543         closesocket(s2);\r
9544         s2 = INVALID_SOCKET;\r
9545         continue;\r
9546       }\r
9547       (void) closesocket(s);\r
9548       (void) closesocket(s2);\r
9549       WSACleanup();\r
9550       return err;\r
9551     }\r
9552     break;\r
9553   }\r
9554   prevStderrPort = fromPort; // remember port used\r
9555   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9556 \r
9557   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9558     err = WSAGetLastError();\r
9559     (void) closesocket(s);\r
9560     (void) closesocket(s2);\r
9561     WSACleanup();\r
9562     return err;\r
9563   }\r
9564 \r
9565   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9566     err = WSAGetLastError();\r
9567     (void) closesocket(s);\r
9568     (void) closesocket(s2);\r
9569     WSACleanup();\r
9570     return err;\r
9571   }\r
9572   if (*user == NULLCHAR) user = UserName();\r
9573   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9574     err = WSAGetLastError();\r
9575     (void) closesocket(s);\r
9576     (void) closesocket(s2);\r
9577     WSACleanup();\r
9578     return err;\r
9579   }\r
9580   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9581     err = WSAGetLastError();\r
9582     (void) closesocket(s);\r
9583     (void) closesocket(s2);\r
9584     WSACleanup();\r
9585     return err;\r
9586   }\r
9587 \r
9588   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9589     err = WSAGetLastError();\r
9590     (void) closesocket(s);\r
9591     (void) closesocket(s2);\r
9592     WSACleanup();\r
9593     return err;\r
9594   }\r
9595   (void) closesocket(s2);  /* Stop listening */\r
9596 \r
9597   /* Prepare return value */\r
9598   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9599   cp->kind = CPRcmd;\r
9600   cp->sock = s;\r
9601   cp->sock2 = s3;\r
9602   *pr = (ProcRef *) cp;\r
9603 \r
9604   return NO_ERROR;\r
9605 }\r
9606 \r
9607 \r
9608 InputSourceRef\r
9609 AddInputSource(ProcRef pr, int lineByLine,\r
9610                InputCallback func, VOIDSTAR closure)\r
9611 {\r
9612   InputSource *is, *is2 = NULL;\r
9613   ChildProc *cp = (ChildProc *) pr;\r
9614 \r
9615   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9616   is->lineByLine = lineByLine;\r
9617   is->func = func;\r
9618   is->closure = closure;\r
9619   is->second = NULL;\r
9620   is->next = is->buf;\r
9621   if (pr == NoProc) {\r
9622     is->kind = CPReal;\r
9623     consoleInputSource = is;\r
9624   } else {\r
9625     is->kind = cp->kind;\r
9626     /* \r
9627         [AS] Try to avoid a race condition if the thread is given control too early:\r
9628         we create all threads suspended so that the is->hThread variable can be\r
9629         safely assigned, then let the threads start with ResumeThread.\r
9630     */\r
9631     switch (cp->kind) {\r
9632     case CPReal:\r
9633       is->hFile = cp->hFrom;\r
9634       cp->hFrom = NULL; /* now owned by InputThread */\r
9635       is->hThread =\r
9636         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9637                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9638       break;\r
9639 \r
9640     case CPComm:\r
9641       is->hFile = cp->hFrom;\r
9642       cp->hFrom = NULL; /* now owned by InputThread */\r
9643       is->hThread =\r
9644         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9645                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9646       break;\r
9647 \r
9648     case CPSock:\r
9649       is->sock = cp->sock;\r
9650       is->hThread =\r
9651         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9652                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9653       break;\r
9654 \r
9655     case CPRcmd:\r
9656       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9657       *is2 = *is;\r
9658       is->sock = cp->sock;\r
9659       is->second = is2;\r
9660       is2->sock = cp->sock2;\r
9661       is2->second = is2;\r
9662       is->hThread =\r
9663         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9664                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9665       is2->hThread =\r
9666         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9667                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9668       break;\r
9669     }\r
9670 \r
9671     if( is->hThread != NULL ) {\r
9672         ResumeThread( is->hThread );\r
9673     }\r
9674 \r
9675     if( is2 != NULL && is2->hThread != NULL ) {\r
9676         ResumeThread( is2->hThread );\r
9677     }\r
9678   }\r
9679 \r
9680   return (InputSourceRef) is;\r
9681 }\r
9682 \r
9683 void\r
9684 RemoveInputSource(InputSourceRef isr)\r
9685 {\r
9686   InputSource *is;\r
9687 \r
9688   is = (InputSource *) isr;\r
9689   is->hThread = NULL;  /* tell thread to stop */\r
9690   CloseHandle(is->hThread);\r
9691   if (is->second != NULL) {\r
9692     is->second->hThread = NULL;\r
9693     CloseHandle(is->second->hThread);\r
9694   }\r
9695 }\r
9696 \r
9697 int no_wrap(char *message, int count)\r
9698 {\r
9699     ConsoleOutput(message, count, FALSE);\r
9700     return count;\r
9701 }\r
9702 \r
9703 int\r
9704 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9705 {\r
9706   DWORD dOutCount;\r
9707   int outCount = SOCKET_ERROR;\r
9708   ChildProc *cp = (ChildProc *) pr;\r
9709   static OVERLAPPED ovl;\r
9710   static int line = 0;\r
9711 \r
9712   if (pr == NoProc)\r
9713   {\r
9714     if (appData.noJoin || !appData.useInternalWrap)\r
9715       return no_wrap(message, count);\r
9716     else\r
9717     {\r
9718       int width = get_term_width();\r
9719       int len = wrap(NULL, message, count, width, &line);\r
9720       char *msg = malloc(len);\r
9721       int dbgchk;\r
9722 \r
9723       if (!msg)\r
9724         return no_wrap(message, count);\r
9725       else\r
9726       {\r
9727         dbgchk = wrap(msg, message, count, width, &line);\r
9728         if (dbgchk != len && appData.debugMode)\r
9729             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9730         ConsoleOutput(msg, len, FALSE);\r
9731         free(msg);\r
9732         return len;\r
9733       }\r
9734     }\r
9735   }\r
9736 \r
9737   if (ovl.hEvent == NULL) {\r
9738     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9739   }\r
9740   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9741 \r
9742   switch (cp->kind) {\r
9743   case CPSock:\r
9744   case CPRcmd:\r
9745     outCount = send(cp->sock, message, count, 0);\r
9746     if (outCount == SOCKET_ERROR) {\r
9747       *outError = WSAGetLastError();\r
9748     } else {\r
9749       *outError = NO_ERROR;\r
9750     }\r
9751     break;\r
9752 \r
9753   case CPReal:\r
9754     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9755                   &dOutCount, NULL)) {\r
9756       *outError = NO_ERROR;\r
9757       outCount = (int) dOutCount;\r
9758     } else {\r
9759       *outError = GetLastError();\r
9760     }\r
9761     break;\r
9762 \r
9763   case CPComm:\r
9764     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9765                             &dOutCount, &ovl);\r
9766     if (*outError == NO_ERROR) {\r
9767       outCount = (int) dOutCount;\r
9768     }\r
9769     break;\r
9770   }\r
9771   return outCount;\r
9772 }\r
9773 \r
9774 void\r
9775 DoSleep(int n)\r
9776 {\r
9777     if(n != 0) Sleep(n);\r
9778 }\r
9779 \r
9780 int\r
9781 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9782                        long msdelay)\r
9783 {\r
9784   /* Ignore delay, not implemented for WinBoard */\r
9785   return OutputToProcess(pr, message, count, outError);\r
9786 }\r
9787 \r
9788 \r
9789 void\r
9790 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9791                         char *buf, int count, int error)\r
9792 {\r
9793   DisplayFatalError(_("Not implemented"), 0, 1);\r
9794 }\r
9795 \r
9796 /* see wgamelist.c for Game List functions */\r
9797 /* see wedittags.c for Edit Tags functions */\r
9798 \r
9799 \r
9800 int\r
9801 ICSInitScript()\r
9802 {\r
9803   FILE *f;\r
9804   char buf[MSG_SIZ];\r
9805   char *dummy;\r
9806 \r
9807   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9808     f = fopen(buf, "r");\r
9809     if (f != NULL) {\r
9810       ProcessICSInitScript(f);\r
9811       fclose(f);\r
9812       return TRUE;\r
9813     }\r
9814   }\r
9815   return FALSE;\r
9816 }\r
9817 \r
9818 \r
9819 VOID\r
9820 StartAnalysisClock()\r
9821 {\r
9822   if (analysisTimerEvent) return;\r
9823   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9824                                         (UINT) 2000, NULL);\r
9825 }\r
9826 \r
9827 VOID\r
9828 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9829 {\r
9830   highlightInfo.sq[0].x = fromX;\r
9831   highlightInfo.sq[0].y = fromY;\r
9832   highlightInfo.sq[1].x = toX;\r
9833   highlightInfo.sq[1].y = toY;\r
9834 }\r
9835 \r
9836 VOID\r
9837 ClearHighlights()\r
9838 {\r
9839   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9840     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9841 }\r
9842 \r
9843 VOID\r
9844 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9845 {\r
9846   premoveHighlightInfo.sq[0].x = fromX;\r
9847   premoveHighlightInfo.sq[0].y = fromY;\r
9848   premoveHighlightInfo.sq[1].x = toX;\r
9849   premoveHighlightInfo.sq[1].y = toY;\r
9850 }\r
9851 \r
9852 VOID\r
9853 ClearPremoveHighlights()\r
9854 {\r
9855   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9856     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9857 }\r
9858 \r
9859 VOID\r
9860 ShutDownFrontEnd()\r
9861 {\r
9862   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9863   DeleteClipboardTempFiles();\r
9864 }\r
9865 \r
9866 void\r
9867 BoardToTop()\r
9868 {\r
9869     if (IsIconic(hwndMain))\r
9870       ShowWindow(hwndMain, SW_RESTORE);\r
9871 \r
9872     SetActiveWindow(hwndMain);\r
9873 }\r
9874 \r
9875 /*\r
9876  * Prototypes for animation support routines\r
9877  */\r
9878 static void ScreenSquare(int column, int row, POINT * pt);\r
9879 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9880      POINT frames[], int * nFrames);\r
9881 \r
9882 \r
9883 #define kFactor 4\r
9884 \r
9885 void\r
9886 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9887 {       // [HGM] atomic: animate blast wave\r
9888         int i;\r
9889 \r
9890         explodeInfo.fromX = fromX;\r
9891         explodeInfo.fromY = fromY;\r
9892         explodeInfo.toX = toX;\r
9893         explodeInfo.toY = toY;\r
9894         for(i=1; i<4*kFactor; i++) {\r
9895             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9896             DrawPosition(FALSE, board);\r
9897             Sleep(appData.animSpeed);\r
9898         }\r
9899         explodeInfo.radius = 0;\r
9900         DrawPosition(TRUE, board);\r
9901 }\r
9902 \r
9903 void\r
9904 AnimateMove(board, fromX, fromY, toX, toY)\r
9905      Board board;\r
9906      int fromX;\r
9907      int fromY;\r
9908      int toX;\r
9909      int toY;\r
9910 {\r
9911   ChessSquare piece;\r
9912   int x = toX, y = toY;\r
9913   POINT start, finish, mid;\r
9914   POINT frames[kFactor * 2 + 1];\r
9915   int nFrames, n;\r
9916 \r
9917   if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();\r
9918 \r
9919   if (!appData.animate) return;\r
9920   if (doingSizing) return;\r
9921   if (fromY < 0 || fromX < 0) return;\r
9922   piece = board[fromY][fromX];\r
9923   if (piece >= EmptySquare) return;\r
9924 \r
9925   if(killX >= 0) toX = killX, toY = killY; // [HGM] lion: first to kill square\r
9926 \r
9927 again:\r
9928 \r
9929   ScreenSquare(fromX, fromY, &start);\r
9930   ScreenSquare(toX, toY, &finish);\r
9931 \r
9932   /* All moves except knight jumps move in straight line */\r
9933   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9934     mid.x = start.x + (finish.x - start.x) / 2;\r
9935     mid.y = start.y + (finish.y - start.y) / 2;\r
9936   } else {\r
9937     /* Knight: make straight movement then diagonal */\r
9938     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9939        mid.x = start.x + (finish.x - start.x) / 2;\r
9940        mid.y = start.y;\r
9941      } else {\r
9942        mid.x = start.x;\r
9943        mid.y = start.y + (finish.y - start.y) / 2;\r
9944      }\r
9945   }\r
9946   \r
9947   /* Don't use as many frames for very short moves */\r
9948   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9949     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9950   else\r
9951     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9952 \r
9953   animInfo.from.x = fromX;\r
9954   animInfo.from.y = fromY;\r
9955   animInfo.to.x = toX;\r
9956   animInfo.to.y = toY;\r
9957   animInfo.lastpos = start;\r
9958   animInfo.piece = piece;\r
9959   for (n = 0; n < nFrames; n++) {\r
9960     animInfo.pos = frames[n];\r
9961     DrawPosition(FALSE, NULL);\r
9962     animInfo.lastpos = animInfo.pos;\r
9963     Sleep(appData.animSpeed);\r
9964   }\r
9965   animInfo.pos = finish;\r
9966   DrawPosition(FALSE, NULL);\r
9967 \r
9968   if(toX != x || toY != y) { fromX = toX; fromY = toY; toX = x; toY = y; goto again; } // second leg\r
9969 \r
9970   animInfo.piece = EmptySquare;\r
9971   Explode(board, fromX, fromY, toX, toY);\r
9972 }\r
9973 \r
9974 /*      Convert board position to corner of screen rect and color       */\r
9975 \r
9976 static void\r
9977 ScreenSquare(column, row, pt)\r
9978      int column; int row; POINT * pt;\r
9979 {\r
9980   if (flipView) {\r
9981     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
9982     pt->y = lineGap + row * (squareSize + lineGap) + border;\r
9983   } else {\r
9984     pt->x = lineGap + column * (squareSize + lineGap) + border;\r
9985     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
9986   }\r
9987 }\r
9988 \r
9989 /*      Generate a series of frame coords from start->mid->finish.\r
9990         The movement rate doubles until the half way point is\r
9991         reached, then halves back down to the final destination,\r
9992         which gives a nice slow in/out effect. The algorithmn\r
9993         may seem to generate too many intermediates for short\r
9994         moves, but remember that the purpose is to attract the\r
9995         viewers attention to the piece about to be moved and\r
9996         then to where it ends up. Too few frames would be less\r
9997         noticeable.                                             */\r
9998 \r
9999 static void\r
10000 Tween(start, mid, finish, factor, frames, nFrames)\r
10001      POINT * start; POINT * mid;\r
10002      POINT * finish; int factor;\r
10003      POINT frames[]; int * nFrames;\r
10004 {\r
10005   int n, fraction = 1, count = 0;\r
10006 \r
10007   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10008   for (n = 0; n < factor; n++)\r
10009     fraction *= 2;\r
10010   for (n = 0; n < factor; n++) {\r
10011     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10012     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10013     count ++;\r
10014     fraction = fraction / 2;\r
10015   }\r
10016   \r
10017   /* Midpoint */\r
10018   frames[count] = *mid;\r
10019   count ++;\r
10020   \r
10021   /* Slow out, stepping 1/2, then 1/4, ... */\r
10022   fraction = 2;\r
10023   for (n = 0; n < factor; n++) {\r
10024     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10025     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10026     count ++;\r
10027     fraction = fraction * 2;\r
10028   }\r
10029   *nFrames = count;\r
10030 }\r
10031 \r
10032 void\r
10033 SettingsPopUp(ChessProgramState *cps)\r
10034 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
10035       EngineOptionsPopup(savedHwnd, cps);\r
10036 }\r
10037 \r
10038 int flock(int fid, int code)\r
10039 {\r
10040     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
10041     OVERLAPPED ov;\r
10042     ov.hEvent = NULL;\r
10043     ov.Offset = 0;\r
10044     ov.OffsetHigh = 0;\r
10045     switch(code) {\r
10046       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
10047       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
10048       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
10049       default: return -1;\r
10050     }\r
10051     return 0;\r
10052 }\r
10053 \r
10054 char *\r
10055 Col2Text (int n)\r
10056 {\r
10057     static int i=0;\r
10058     static char col[8][20];\r
10059     COLORREF color = *(COLORREF *) colorVariable[n];\r
10060     i = i+1 & 7;\r
10061     snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
10062     return col[i];\r
10063 }\r
10064 \r
10065 void\r
10066 ActivateTheme (int new)\r
10067 {   // Redo initialization of features depending on options that can occur in themes\r
10068    InitTextures();\r
10069    if(new) InitDrawingColors();\r
10070    fontBitmapSquareSize = 0; // request creation of new font pieces\r
10071    InitDrawingSizes(boardSize, 0);\r
10072    InvalidateRect(hwndMain, NULL, TRUE);\r
10073 }\r