bf98cc0805ddde51aeb152eb0110456a0a84112e
[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 static int promoStyle;\r
4449 \r
4450 /* Process messages for Promotion dialog box */\r
4451 LRESULT CALLBACK\r
4452 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4453 {\r
4454   char promoChar;\r
4455 \r
4456   switch (message) {\r
4457   case WM_INITDIALOG: /* message: initialize dialog box */\r
4458     /* Center the dialog over the application window */\r
4459     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4460     Translate(hDlg, DLG_PromotionKing);\r
4461     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4462       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4463        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4464        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4465                SW_SHOW : SW_HIDE);\r
4466     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4467     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4468        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4469          PieceToChar(WhiteAngel) != '~') ||\r
4470         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4471          PieceToChar(BlackAngel) != '~')   ) ?\r
4472                SW_SHOW : SW_HIDE);\r
4473     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4474        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4475          PieceToChar(WhiteMarshall) != '~') ||\r
4476         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4477          PieceToChar(BlackMarshall) != '~')   ) ?\r
4478                SW_SHOW : SW_HIDE);\r
4479     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4480     ShowWindow(GetDlgItem(hDlg, PB_Rook),   !style ? SW_SHOW : SW_HIDE);\r
4481     ShowWindow(GetDlgItem(hDlg, PB_Bishop), !style ? SW_SHOW : SW_HIDE);\r
4482     if(style) {\r
4483         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4484         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4485         SetWindowText(hDlg, "Promote?");\r
4486     }\r
4487     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4488        gameInfo.variant == VariantSuper ?\r
4489                SW_SHOW : SW_HIDE);\r
4490     return TRUE;\r
4491 \r
4492   case WM_COMMAND: /* message: received a command */\r
4493     switch (LOWORD(wParam)) {\r
4494     case IDCANCEL:\r
4495       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4496       ClearHighlights();\r
4497       DrawPosition(FALSE, NULL);\r
4498       return TRUE;\r
4499     case PB_King:\r
4500       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4501       break;\r
4502     case PB_Queen:\r
4503       promoChar = style ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4504       break;\r
4505     case PB_Rook:\r
4506       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4507       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4508       break;\r
4509     case PB_Bishop:\r
4510       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4511       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4512       break;\r
4513     case PB_Chancellor:\r
4514       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4515       break;\r
4516     case PB_Archbishop:\r
4517       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4518       break;\r
4519     case PB_Knight:\r
4520       promoChar = gameInfo.variant == VariantShogi ? '=' : style ? NULLCHAR : \r
4521                   ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight));\r
4522       break;\r
4523     default:\r
4524       return FALSE;\r
4525     }\r
4526     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4527     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4528     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4529     fromX = fromY = -1;\r
4530     if (!appData.highlightLastMove) {\r
4531       ClearHighlights();\r
4532       DrawPosition(FALSE, NULL);\r
4533     }\r
4534     return TRUE;\r
4535   }\r
4536   return FALSE;\r
4537 }\r
4538 \r
4539 /* Pop up promotion dialog */\r
4540 VOID\r
4541 PromotionPopup(HWND hwnd)\r
4542 {\r
4543   FARPROC lpProc;\r
4544 \r
4545   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4546   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4547     hwnd, (DLGPROC)lpProc);\r
4548   FreeProcInstance(lpProc);\r
4549 }\r
4550 \r
4551 void\r
4552 PromotionPopUp(char choice)\r
4553 {\r
4554   promoStyle = (choice == '+');\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 \r
6851 VOID\r
6852 SaveInHistory(char *cmd)\r
6853 {\r
6854   if (history[histIn] != NULL) {\r
6855     free(history[histIn]);\r
6856     history[histIn] = NULL;\r
6857   }\r
6858   if (*cmd == NULLCHAR) return;\r
6859   history[histIn] = StrSave(cmd);\r
6860   histIn = (histIn + 1) % HISTORY_SIZE;\r
6861   if (history[histIn] != NULL) {\r
6862     free(history[histIn]);\r
6863 \r
6864     history[histIn] = NULL;\r
6865   }\r
6866   histP = histIn;\r
6867 }\r
6868 \r
6869 char *\r
6870 PrevInHistory(char *cmd)\r
6871 {\r
6872   int newhp;\r
6873   if (histP == histIn) {\r
6874     if (history[histIn] != NULL) free(history[histIn]);\r
6875     history[histIn] = StrSave(cmd);\r
6876   }\r
6877   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6878   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6879   histP = newhp;\r
6880   return history[histP];\r
6881 }\r
6882 \r
6883 char *\r
6884 NextInHistory()\r
6885 {\r
6886   if (histP == histIn) return NULL;\r
6887   histP = (histP + 1) % HISTORY_SIZE;\r
6888   return history[histP];   \r
6889 }\r
6890 \r
6891 HMENU\r
6892 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6893 {\r
6894   HMENU hmenu, h;\r
6895   int i = 0;\r
6896   hmenu = LoadMenu(hInst, "TextMenu");\r
6897   h = GetSubMenu(hmenu, 0);\r
6898   while (e->item) {\r
6899     if (strcmp(e->item, "-") == 0) {\r
6900       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6901     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6902       int flags = MF_STRING, j = 0;\r
6903       if (e->item[0] == '|') {\r
6904         flags |= MF_MENUBARBREAK;\r
6905         j++;\r
6906       }\r
6907       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6908       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6909     }\r
6910     e++;\r
6911     i++;\r
6912   } \r
6913   return hmenu;\r
6914 }\r
6915 \r
6916 WNDPROC consoleTextWindowProc;\r
6917 \r
6918 void\r
6919 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6920 {\r
6921   char buf[MSG_SIZ], name[MSG_SIZ];\r
6922   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6923   CHARRANGE sel;\r
6924 \r
6925   if (!getname) {\r
6926     SetWindowText(hInput, command);\r
6927     if (immediate) {\r
6928       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6929     } else {\r
6930       sel.cpMin = 999999;\r
6931       sel.cpMax = 999999;\r
6932       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6933       SetFocus(hInput);\r
6934     }\r
6935     return;\r
6936   }    \r
6937   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6938   if (sel.cpMin == sel.cpMax) {\r
6939     /* Expand to surrounding word */\r
6940     TEXTRANGE tr;\r
6941     do {\r
6942       tr.chrg.cpMax = sel.cpMin;\r
6943       tr.chrg.cpMin = --sel.cpMin;\r
6944       if (sel.cpMin < 0) break;\r
6945       tr.lpstrText = name;\r
6946       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6947     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6948     sel.cpMin++;\r
6949 \r
6950     do {\r
6951       tr.chrg.cpMin = sel.cpMax;\r
6952       tr.chrg.cpMax = ++sel.cpMax;\r
6953       tr.lpstrText = name;\r
6954       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6955     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6956     sel.cpMax--;\r
6957 \r
6958     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6959       MessageBeep(MB_ICONEXCLAMATION);\r
6960       return;\r
6961     }\r
6962     tr.chrg = sel;\r
6963     tr.lpstrText = name;\r
6964     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6965   } else {\r
6966     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6967       MessageBeep(MB_ICONEXCLAMATION);\r
6968       return;\r
6969     }\r
6970     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6971   }\r
6972   if (immediate) {\r
6973     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6974     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6975     SetWindowText(hInput, buf);\r
6976     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6977   } else {\r
6978     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6979       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6980     SetWindowText(hInput, buf);\r
6981     sel.cpMin = 999999;\r
6982     sel.cpMax = 999999;\r
6983     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6984     SetFocus(hInput);\r
6985   }\r
6986 }\r
6987 \r
6988 LRESULT CALLBACK \r
6989 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6990 {\r
6991   HWND hInput;\r
6992   CHARRANGE sel;\r
6993 \r
6994   switch (message) {\r
6995   case WM_KEYDOWN:\r
6996     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6997     if(wParam=='R') return 0;\r
6998     switch (wParam) {\r
6999     case VK_PRIOR:\r
7000       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7001       return 0;\r
7002     case VK_NEXT:\r
7003       sel.cpMin = 999999;\r
7004       sel.cpMax = 999999;\r
7005       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7006       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7007       return 0;\r
7008     }\r
7009     break;\r
7010   case WM_CHAR:\r
7011    if(wParam != '\022') {\r
7012     if (wParam == '\t') {\r
7013       if (GetKeyState(VK_SHIFT) < 0) {\r
7014         /* shifted */\r
7015         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7016         if (buttonDesc[0].hwnd) {\r
7017           SetFocus(buttonDesc[0].hwnd);\r
7018         } else {\r
7019           SetFocus(hwndMain);\r
7020         }\r
7021       } else {\r
7022         /* unshifted */\r
7023         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7024       }\r
7025     } else {\r
7026       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7027       JAWS_DELETE( SetFocus(hInput); )\r
7028       SendMessage(hInput, message, wParam, lParam);\r
7029     }\r
7030     return 0;\r
7031    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
7032    lParam = -1;\r
7033   case WM_RBUTTONDOWN:\r
7034     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7035       /* Move selection here if it was empty */\r
7036       POINT pt;\r
7037       pt.x = LOWORD(lParam);\r
7038       pt.y = HIWORD(lParam);\r
7039       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7040       if (sel.cpMin == sel.cpMax) {\r
7041         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7042         sel.cpMax = sel.cpMin;\r
7043         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7044       }\r
7045       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7046 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
7047       POINT pt;\r
7048       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7049       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7050       if (sel.cpMin == sel.cpMax) {\r
7051         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7052         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7053       }\r
7054       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7055         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7056       }\r
7057       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
7058       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
7059       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
7060       MenuPopup(hwnd, pt, hmenu, -1);\r
7061 }\r
7062     }\r
7063     return 0;\r
7064   case WM_RBUTTONUP:\r
7065     if (GetKeyState(VK_SHIFT) & ~1) {\r
7066       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7067         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7068     }\r
7069     return 0;\r
7070   case WM_PASTE:\r
7071     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7072     SetFocus(hInput);\r
7073     return SendMessage(hInput, message, wParam, lParam);\r
7074   case WM_MBUTTONDOWN:\r
7075     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7076   case WM_COMMAND:\r
7077     switch (LOWORD(wParam)) {\r
7078     case IDM_QuickPaste:\r
7079       {\r
7080         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7081         if (sel.cpMin == sel.cpMax) {\r
7082           MessageBeep(MB_ICONEXCLAMATION);\r
7083           return 0;\r
7084         }\r
7085         SendMessage(hwnd, WM_COPY, 0, 0);\r
7086         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7087         SendMessage(hInput, WM_PASTE, 0, 0);\r
7088         SetFocus(hInput);\r
7089         return 0;\r
7090       }\r
7091     case IDM_Cut:\r
7092       SendMessage(hwnd, WM_CUT, 0, 0);\r
7093       return 0;\r
7094     case IDM_Paste:\r
7095       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7096       return 0;\r
7097     case IDM_Copy:\r
7098       SendMessage(hwnd, WM_COPY, 0, 0);\r
7099       return 0;\r
7100     default:\r
7101       {\r
7102         int i = LOWORD(wParam) - IDM_CommandX;\r
7103         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7104             icsTextMenuEntry[i].command != NULL) {\r
7105           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7106                    icsTextMenuEntry[i].getname,\r
7107                    icsTextMenuEntry[i].immediate);\r
7108           return 0;\r
7109         }\r
7110       }\r
7111       break;\r
7112     }\r
7113     break;\r
7114   }\r
7115   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7116 }\r
7117 \r
7118 WNDPROC consoleInputWindowProc;\r
7119 \r
7120 LRESULT CALLBACK\r
7121 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7122 {\r
7123   char buf[MSG_SIZ];\r
7124   char *p;\r
7125   static BOOL sendNextChar = FALSE;\r
7126   static BOOL quoteNextChar = FALSE;\r
7127   InputSource *is = consoleInputSource;\r
7128   CHARFORMAT cf;\r
7129   CHARRANGE sel;\r
7130 \r
7131   switch (message) {\r
7132   case WM_CHAR:\r
7133     if (!appData.localLineEditing || sendNextChar) {\r
7134       is->buf[0] = (CHAR) wParam;\r
7135       is->count = 1;\r
7136       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7137       sendNextChar = FALSE;\r
7138       return 0;\r
7139     }\r
7140     if (quoteNextChar) {\r
7141       buf[0] = (char) wParam;\r
7142       buf[1] = NULLCHAR;\r
7143       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7144       quoteNextChar = FALSE;\r
7145       return 0;\r
7146     }\r
7147     switch (wParam) {\r
7148     case '\r':   /* Enter key */\r
7149       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7150       if (consoleEcho) SaveInHistory(is->buf);\r
7151       is->buf[is->count++] = '\n';\r
7152       is->buf[is->count] = NULLCHAR;\r
7153       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7154       if (consoleEcho) {\r
7155         ConsoleOutput(is->buf, is->count, TRUE);\r
7156       } else if (appData.localLineEditing) {\r
7157         ConsoleOutput("\n", 1, TRUE);\r
7158       }\r
7159       /* fall thru */\r
7160     case '\033': /* Escape key */\r
7161       SetWindowText(hwnd, "");\r
7162       cf.cbSize = sizeof(CHARFORMAT);\r
7163       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7164       if (consoleEcho) {\r
7165         cf.crTextColor = textAttribs[ColorNormal].color;\r
7166       } else {\r
7167         cf.crTextColor = COLOR_ECHOOFF;\r
7168       }\r
7169       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7170       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7171       return 0;\r
7172     case '\t':   /* Tab key */\r
7173       if (GetKeyState(VK_SHIFT) < 0) {\r
7174         /* shifted */\r
7175         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7176       } else {\r
7177         /* unshifted */\r
7178         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7179         if (buttonDesc[0].hwnd) {\r
7180           SetFocus(buttonDesc[0].hwnd);\r
7181         } else {\r
7182           SetFocus(hwndMain);\r
7183         }\r
7184       }\r
7185       return 0;\r
7186     case '\023': /* Ctrl+S */\r
7187       sendNextChar = TRUE;\r
7188       return 0;\r
7189     case '\021': /* Ctrl+Q */\r
7190       quoteNextChar = TRUE;\r
7191       return 0;\r
7192     JAWS_REPLAY\r
7193     default:\r
7194       break;\r
7195     }\r
7196     break;\r
7197   case WM_KEYDOWN:\r
7198     switch (wParam) {\r
7199     case VK_UP:\r
7200       GetWindowText(hwnd, buf, MSG_SIZ);\r
7201       p = PrevInHistory(buf);\r
7202       if (p != NULL) {\r
7203         SetWindowText(hwnd, p);\r
7204         sel.cpMin = 999999;\r
7205         sel.cpMax = 999999;\r
7206         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7207         return 0;\r
7208       }\r
7209       break;\r
7210     case VK_DOWN:\r
7211       p = NextInHistory();\r
7212       if (p != NULL) {\r
7213         SetWindowText(hwnd, p);\r
7214         sel.cpMin = 999999;\r
7215         sel.cpMax = 999999;\r
7216         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7217         return 0;\r
7218       }\r
7219       break;\r
7220     case VK_HOME:\r
7221     case VK_END:\r
7222       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7223       /* fall thru */\r
7224     case VK_PRIOR:\r
7225     case VK_NEXT:\r
7226       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7227       return 0;\r
7228     }\r
7229     break;\r
7230   case WM_MBUTTONDOWN:\r
7231     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7232       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7233     break;\r
7234   case WM_RBUTTONUP:\r
7235     if (GetKeyState(VK_SHIFT) & ~1) {\r
7236       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7237         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7238     } else {\r
7239       POINT pt;\r
7240       HMENU hmenu;\r
7241       hmenu = LoadMenu(hInst, "InputMenu");\r
7242       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7243       if (sel.cpMin == sel.cpMax) {\r
7244         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7245         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7246       }\r
7247       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7248         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7249       }\r
7250       pt.x = LOWORD(lParam);\r
7251       pt.y = HIWORD(lParam);\r
7252       MenuPopup(hwnd, pt, hmenu, -1);\r
7253     }\r
7254     return 0;\r
7255   case WM_COMMAND:\r
7256     switch (LOWORD(wParam)) { \r
7257     case IDM_Undo:\r
7258       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7259       return 0;\r
7260     case IDM_SelectAll:\r
7261       sel.cpMin = 0;\r
7262       sel.cpMax = -1; /*999999?*/\r
7263       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7264       return 0;\r
7265     case IDM_Cut:\r
7266       SendMessage(hwnd, WM_CUT, 0, 0);\r
7267       return 0;\r
7268     case IDM_Paste:\r
7269       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7270       return 0;\r
7271     case IDM_Copy:\r
7272       SendMessage(hwnd, WM_COPY, 0, 0);\r
7273       return 0;\r
7274     }\r
7275     break;\r
7276   }\r
7277   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7278 }\r
7279 \r
7280 #define CO_MAX  100000\r
7281 #define CO_TRIM   1000\r
7282 \r
7283 LRESULT CALLBACK\r
7284 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7285 {\r
7286   static SnapData sd;\r
7287   HWND hText, hInput;\r
7288   RECT rect;\r
7289   static int sizeX, sizeY;\r
7290   int newSizeX, newSizeY;\r
7291   MINMAXINFO *mmi;\r
7292   WORD wMask;\r
7293 \r
7294   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7295   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7296 \r
7297   switch (message) {\r
7298   case WM_NOTIFY:\r
7299     if (((NMHDR*)lParam)->code == EN_LINK)\r
7300     {\r
7301       ENLINK *pLink = (ENLINK*)lParam;\r
7302       if (pLink->msg == WM_LBUTTONUP)\r
7303       {\r
7304         TEXTRANGE tr;\r
7305 \r
7306         tr.chrg = pLink->chrg;\r
7307         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7308         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7309         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7310         free(tr.lpstrText);\r
7311       }\r
7312     }\r
7313     break;\r
7314   case WM_INITDIALOG: /* message: initialize dialog box */\r
7315     hwndConsole = hDlg;\r
7316     SetFocus(hInput);\r
7317     consoleTextWindowProc = (WNDPROC)\r
7318       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7319     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7320     consoleInputWindowProc = (WNDPROC)\r
7321       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7322     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7323     Colorize(ColorNormal, TRUE);\r
7324     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7325     ChangedConsoleFont();\r
7326     GetClientRect(hDlg, &rect);\r
7327     sizeX = rect.right;\r
7328     sizeY = rect.bottom;\r
7329     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7330         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7331       WINDOWPLACEMENT wp;\r
7332       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7333       wp.length = sizeof(WINDOWPLACEMENT);\r
7334       wp.flags = 0;\r
7335       wp.showCmd = SW_SHOW;\r
7336       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7337       wp.rcNormalPosition.left = wpConsole.x;\r
7338       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7339       wp.rcNormalPosition.top = wpConsole.y;\r
7340       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7341       SetWindowPlacement(hDlg, &wp);\r
7342     }\r
7343 \r
7344    // [HGM] Chessknight's change 2004-07-13\r
7345    else { /* Determine Defaults */\r
7346        WINDOWPLACEMENT wp;\r
7347        wpConsole.x = wpMain.width + 1;\r
7348        wpConsole.y = wpMain.y;\r
7349        wpConsole.width = screenWidth -  wpMain.width;\r
7350        wpConsole.height = wpMain.height;\r
7351        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7352        wp.length = sizeof(WINDOWPLACEMENT);\r
7353        wp.flags = 0;\r
7354        wp.showCmd = SW_SHOW;\r
7355        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7356        wp.rcNormalPosition.left = wpConsole.x;\r
7357        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7358        wp.rcNormalPosition.top = wpConsole.y;\r
7359        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7360        SetWindowPlacement(hDlg, &wp);\r
7361     }\r
7362 \r
7363    // Allow hText to highlight URLs and send notifications on them\r
7364    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7365    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7366    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7367    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7368 \r
7369     return FALSE;\r
7370 \r
7371   case WM_SETFOCUS:\r
7372     SetFocus(hInput);\r
7373     return 0;\r
7374 \r
7375   case WM_CLOSE:\r
7376     ExitEvent(0);\r
7377     /* not reached */\r
7378     break;\r
7379 \r
7380   case WM_SIZE:\r
7381     if (IsIconic(hDlg)) break;\r
7382     newSizeX = LOWORD(lParam);\r
7383     newSizeY = HIWORD(lParam);\r
7384     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7385       RECT rectText, rectInput;\r
7386       POINT pt;\r
7387       int newTextHeight, newTextWidth;\r
7388       GetWindowRect(hText, &rectText);\r
7389       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7390       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7391       if (newTextHeight < 0) {\r
7392         newSizeY += -newTextHeight;\r
7393         newTextHeight = 0;\r
7394       }\r
7395       SetWindowPos(hText, NULL, 0, 0,\r
7396         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7397       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7398       pt.x = rectInput.left;\r
7399       pt.y = rectInput.top + newSizeY - sizeY;\r
7400       ScreenToClient(hDlg, &pt);\r
7401       SetWindowPos(hInput, NULL, \r
7402         pt.x, pt.y, /* needs client coords */   \r
7403         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7404         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7405     }\r
7406     sizeX = newSizeX;\r
7407     sizeY = newSizeY;\r
7408     break;\r
7409 \r
7410   case WM_GETMINMAXINFO:\r
7411     /* Prevent resizing window too small */\r
7412     mmi = (MINMAXINFO *) lParam;\r
7413     mmi->ptMinTrackSize.x = 100;\r
7414     mmi->ptMinTrackSize.y = 100;\r
7415     break;\r
7416 \r
7417   /* [AS] Snapping */\r
7418   case WM_ENTERSIZEMOVE:\r
7419     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7420 \r
7421   case WM_SIZING:\r
7422     return OnSizing( &sd, hDlg, wParam, lParam );\r
7423 \r
7424   case WM_MOVING:\r
7425     return OnMoving( &sd, hDlg, wParam, lParam );\r
7426 \r
7427   case WM_EXITSIZEMOVE:\r
7428         UpdateICSWidth(hText);\r
7429     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7430   }\r
7431 \r
7432   return DefWindowProc(hDlg, message, wParam, lParam);\r
7433 }\r
7434 \r
7435 \r
7436 VOID\r
7437 ConsoleCreate()\r
7438 {\r
7439   HWND hCons;\r
7440   if (hwndConsole) return;\r
7441   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7442   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7443 }\r
7444 \r
7445 \r
7446 VOID\r
7447 ConsoleOutput(char* data, int length, int forceVisible)\r
7448 {\r
7449   HWND hText;\r
7450   int trim, exlen;\r
7451   char *p, *q;\r
7452   char buf[CO_MAX+1];\r
7453   POINT pEnd;\r
7454   RECT rect;\r
7455   static int delayLF = 0;\r
7456   CHARRANGE savesel, sel;\r
7457 \r
7458   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7459   p = data;\r
7460   q = buf;\r
7461   if (delayLF) {\r
7462     *q++ = '\r';\r
7463     *q++ = '\n';\r
7464     delayLF = 0;\r
7465   }\r
7466   while (length--) {\r
7467     if (*p == '\n') {\r
7468       if (*++p) {\r
7469         *q++ = '\r';\r
7470         *q++ = '\n';\r
7471       } else {\r
7472         delayLF = 1;\r
7473       }\r
7474     } else if (*p == '\007') {\r
7475        MyPlaySound(&sounds[(int)SoundBell]);\r
7476        p++;\r
7477     } else {\r
7478       *q++ = *p++;\r
7479     }\r
7480   }\r
7481   *q = NULLCHAR;\r
7482   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7483   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7484   /* Save current selection */\r
7485   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7486   exlen = GetWindowTextLength(hText);\r
7487   /* Find out whether current end of text is visible */\r
7488   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7489   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7490   /* Trim existing text if it's too long */\r
7491   if (exlen + (q - buf) > CO_MAX) {\r
7492     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7493     sel.cpMin = 0;\r
7494     sel.cpMax = trim;\r
7495     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7496     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7497     exlen -= trim;\r
7498     savesel.cpMin -= trim;\r
7499     savesel.cpMax -= trim;\r
7500     if (exlen < 0) exlen = 0;\r
7501     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7502     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7503   }\r
7504   /* Append the new text */\r
7505   sel.cpMin = exlen;\r
7506   sel.cpMax = exlen;\r
7507   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7508   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7509   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7510   if (forceVisible || exlen == 0 ||\r
7511       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7512        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7513     /* Scroll to make new end of text visible if old end of text\r
7514        was visible or new text is an echo of user typein */\r
7515     sel.cpMin = 9999999;\r
7516     sel.cpMax = 9999999;\r
7517     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7518     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7519     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7520     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7521   }\r
7522   if (savesel.cpMax == exlen || forceVisible) {\r
7523     /* Move insert point to new end of text if it was at the old\r
7524        end of text or if the new text is an echo of user typein */\r
7525     sel.cpMin = 9999999;\r
7526     sel.cpMax = 9999999;\r
7527     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7528   } else {\r
7529     /* Restore previous selection */\r
7530     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7531   }\r
7532   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7533 }\r
7534 \r
7535 /*---------*/\r
7536 \r
7537 \r
7538 void\r
7539 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7540 {\r
7541   char buf[100];\r
7542   char *str;\r
7543   COLORREF oldFg, oldBg;\r
7544   HFONT oldFont;\r
7545   RECT rect;\r
7546 \r
7547   if(copyNumber > 1)\r
7548     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7549 \r
7550   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7551   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7552   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7553 \r
7554   rect.left = x;\r
7555   rect.right = x + squareSize;\r
7556   rect.top  = y;\r
7557   rect.bottom = y + squareSize;\r
7558   str = buf;\r
7559 \r
7560   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7561                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7562              y, ETO_CLIPPED|ETO_OPAQUE,\r
7563              &rect, str, strlen(str), NULL);\r
7564 \r
7565   (void) SetTextColor(hdc, oldFg);\r
7566   (void) SetBkColor(hdc, oldBg);\r
7567   (void) SelectObject(hdc, oldFont);\r
7568 }\r
7569 \r
7570 void\r
7571 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7572               RECT *rect, char *color, char *flagFell)\r
7573 {\r
7574   char buf[100];\r
7575   char *str;\r
7576   COLORREF oldFg, oldBg;\r
7577   HFONT oldFont;\r
7578 \r
7579   if (twoBoards && partnerUp) return;\r
7580   if (appData.clockMode) {\r
7581     if (tinyLayout)\r
7582       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7583     else\r
7584       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7585     str = buf;\r
7586   } else {\r
7587     str = color;\r
7588   }\r
7589 \r
7590   if (highlight) {\r
7591     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7592     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7593   } else {\r
7594     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7595     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7596   }\r
7597   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7598 \r
7599   JAWS_SILENCE\r
7600 \r
7601   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7602              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7603              rect, str, strlen(str), NULL);\r
7604   if(logoHeight > 0 && appData.clockMode) {\r
7605       RECT r;\r
7606       str += strlen(color)+2;\r
7607       r.top = rect->top + logoHeight/2;\r
7608       r.left = rect->left;\r
7609       r.right = rect->right;\r
7610       r.bottom = rect->bottom;\r
7611       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7612                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7613                  &r, str, strlen(str), NULL);\r
7614   }\r
7615   (void) SetTextColor(hdc, oldFg);\r
7616   (void) SetBkColor(hdc, oldBg);\r
7617   (void) SelectObject(hdc, oldFont);\r
7618 }\r
7619 \r
7620 \r
7621 int\r
7622 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7623            OVERLAPPED *ovl)\r
7624 {\r
7625   int ok, err;\r
7626 \r
7627   /* [AS]  */\r
7628   if( count <= 0 ) {\r
7629     if (appData.debugMode) {\r
7630       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7631     }\r
7632 \r
7633     return ERROR_INVALID_USER_BUFFER;\r
7634   }\r
7635 \r
7636   ResetEvent(ovl->hEvent);\r
7637   ovl->Offset = ovl->OffsetHigh = 0;\r
7638   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7639   if (ok) {\r
7640     err = NO_ERROR;\r
7641   } else {\r
7642     err = GetLastError();\r
7643     if (err == ERROR_IO_PENDING) {\r
7644       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7645       if (ok)\r
7646         err = NO_ERROR;\r
7647       else\r
7648         err = GetLastError();\r
7649     }\r
7650   }\r
7651   return err;\r
7652 }\r
7653 \r
7654 int\r
7655 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7656             OVERLAPPED *ovl)\r
7657 {\r
7658   int ok, err;\r
7659 \r
7660   ResetEvent(ovl->hEvent);\r
7661   ovl->Offset = ovl->OffsetHigh = 0;\r
7662   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7663   if (ok) {\r
7664     err = NO_ERROR;\r
7665   } else {\r
7666     err = GetLastError();\r
7667     if (err == ERROR_IO_PENDING) {\r
7668       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7669       if (ok)\r
7670         err = NO_ERROR;\r
7671       else\r
7672         err = GetLastError();\r
7673     }\r
7674   }\r
7675   return err;\r
7676 }\r
7677 \r
7678 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7679 void CheckForInputBufferFull( InputSource * is )\r
7680 {\r
7681     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7682         /* Look for end of line */\r
7683         char * p = is->buf;\r
7684         \r
7685         while( p < is->next && *p != '\n' ) {\r
7686             p++;\r
7687         }\r
7688 \r
7689         if( p >= is->next ) {\r
7690             if (appData.debugMode) {\r
7691                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7692             }\r
7693 \r
7694             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7695             is->count = (DWORD) -1;\r
7696             is->next = is->buf;\r
7697         }\r
7698     }\r
7699 }\r
7700 \r
7701 DWORD\r
7702 InputThread(LPVOID arg)\r
7703 {\r
7704   InputSource *is;\r
7705   OVERLAPPED ovl;\r
7706 \r
7707   is = (InputSource *) arg;\r
7708   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7709   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7710   while (is->hThread != NULL) {\r
7711     is->error = DoReadFile(is->hFile, is->next,\r
7712                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7713                            &is->count, &ovl);\r
7714     if (is->error == NO_ERROR) {\r
7715       is->next += is->count;\r
7716     } else {\r
7717       if (is->error == ERROR_BROKEN_PIPE) {\r
7718         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7719         is->count = 0;\r
7720       } else {\r
7721         is->count = (DWORD) -1;\r
7722         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7723         break; \r
7724       }\r
7725     }\r
7726 \r
7727     CheckForInputBufferFull( is );\r
7728 \r
7729     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7730 \r
7731     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7732 \r
7733     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7734   }\r
7735 \r
7736   CloseHandle(ovl.hEvent);\r
7737   CloseHandle(is->hFile);\r
7738 \r
7739   if (appData.debugMode) {\r
7740     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7741   }\r
7742 \r
7743   return 0;\r
7744 }\r
7745 \r
7746 \r
7747 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7748 DWORD\r
7749 NonOvlInputThread(LPVOID arg)\r
7750 {\r
7751   InputSource *is;\r
7752   char *p, *q;\r
7753   int i;\r
7754   char prev;\r
7755 \r
7756   is = (InputSource *) arg;\r
7757   while (is->hThread != NULL) {\r
7758     is->error = ReadFile(is->hFile, is->next,\r
7759                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7760                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7761     if (is->error == NO_ERROR) {\r
7762       /* Change CRLF to LF */\r
7763       if (is->next > is->buf) {\r
7764         p = is->next - 1;\r
7765         i = is->count + 1;\r
7766       } else {\r
7767         p = is->next;\r
7768         i = is->count;\r
7769       }\r
7770       q = p;\r
7771       prev = NULLCHAR;\r
7772       while (i > 0) {\r
7773         if (prev == '\r' && *p == '\n') {\r
7774           *(q-1) = '\n';\r
7775           is->count--;\r
7776         } else { \r
7777           *q++ = *p;\r
7778         }\r
7779         prev = *p++;\r
7780         i--;\r
7781       }\r
7782       *q = NULLCHAR;\r
7783       is->next = q;\r
7784     } else {\r
7785       if (is->error == ERROR_BROKEN_PIPE) {\r
7786         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7787         is->count = 0; \r
7788       } else {\r
7789         is->count = (DWORD) -1;\r
7790       }\r
7791     }\r
7792 \r
7793     CheckForInputBufferFull( is );\r
7794 \r
7795     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7796 \r
7797     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7798 \r
7799     if (is->count < 0) break;  /* Quit on error */\r
7800   }\r
7801   CloseHandle(is->hFile);\r
7802   return 0;\r
7803 }\r
7804 \r
7805 DWORD\r
7806 SocketInputThread(LPVOID arg)\r
7807 {\r
7808   InputSource *is;\r
7809 \r
7810   is = (InputSource *) arg;\r
7811   while (is->hThread != NULL) {\r
7812     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7813     if ((int)is->count == SOCKET_ERROR) {\r
7814       is->count = (DWORD) -1;\r
7815       is->error = WSAGetLastError();\r
7816     } else {\r
7817       is->error = NO_ERROR;\r
7818       is->next += is->count;\r
7819       if (is->count == 0 && is->second == is) {\r
7820         /* End of file on stderr; quit with no message */\r
7821         break;\r
7822       }\r
7823     }\r
7824     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7825 \r
7826     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7827 \r
7828     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7829   }\r
7830   return 0;\r
7831 }\r
7832 \r
7833 VOID\r
7834 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7835 {\r
7836   InputSource *is;\r
7837 \r
7838   is = (InputSource *) lParam;\r
7839   if (is->lineByLine) {\r
7840     /* Feed in lines one by one */\r
7841     char *p = is->buf;\r
7842     char *q = p;\r
7843     while (q < is->next) {\r
7844       if (*q++ == '\n') {\r
7845         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7846         p = q;\r
7847       }\r
7848     }\r
7849     \r
7850     /* Move any partial line to the start of the buffer */\r
7851     q = is->buf;\r
7852     while (p < is->next) {\r
7853       *q++ = *p++;\r
7854     }\r
7855     is->next = q;\r
7856 \r
7857     if (is->error != NO_ERROR || is->count == 0) {\r
7858       /* Notify backend of the error.  Note: If there was a partial\r
7859          line at the end, it is not flushed through. */\r
7860       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7861     }\r
7862   } else {\r
7863     /* Feed in the whole chunk of input at once */\r
7864     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7865     is->next = is->buf;\r
7866   }\r
7867 }\r
7868 \r
7869 /*---------------------------------------------------------------------------*\\r
7870  *\r
7871  *  Menu enables. Used when setting various modes.\r
7872  *\r
7873 \*---------------------------------------------------------------------------*/\r
7874 \r
7875 typedef struct {\r
7876   int item;\r
7877   int flags;\r
7878 } Enables;\r
7879 \r
7880 VOID\r
7881 GreyRevert(Boolean grey)\r
7882 { // [HGM] vari: for retracting variations in local mode\r
7883   HMENU hmenu = GetMenu(hwndMain);\r
7884   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7885   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7886 }\r
7887 \r
7888 VOID\r
7889 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7890 {\r
7891   while (enab->item > 0) {\r
7892     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7893     enab++;\r
7894   }\r
7895 }\r
7896 \r
7897 Enables gnuEnables[] = {\r
7898   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7899   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7900   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7901   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7902   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7903   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7904   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7905   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7906   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7907   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7908   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7909   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7910   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7911 \r
7912   // Needed to switch from ncp to GNU mode on Engine Load\r
7913   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7914   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7915   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7916   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7917   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7918   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7919   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7920   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7921   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7922   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7923   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7924   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7925   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7926   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7927   { -1, -1 }\r
7928 };\r
7929 \r
7930 Enables icsEnables[] = {\r
7931   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7932   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7933   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7934   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7935   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7936   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7937   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7938   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7939   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7940   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7941   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7942   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7943   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7944   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
7945   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
7946   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7947   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7948   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7949   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7950   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
7951   { -1, -1 }\r
7952 };\r
7953 \r
7954 #if ZIPPY\r
7955 Enables zippyEnables[] = {\r
7956   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7957   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7958   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7959   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7960   { -1, -1 }\r
7961 };\r
7962 #endif\r
7963 \r
7964 Enables ncpEnables[] = {\r
7965   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7966   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7967   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7968   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7969   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7970   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7971   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7972   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7973   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7974   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7975   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7976   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7977   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7978   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7979   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7980   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7981   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7982   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7983   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7984   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7985   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7986   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7987   { -1, -1 }\r
7988 };\r
7989 \r
7990 Enables trainingOnEnables[] = {\r
7991   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7992   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7993   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7994   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7995   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7996   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7997   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7998   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7999   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8000   { -1, -1 }\r
8001 };\r
8002 \r
8003 Enables trainingOffEnables[] = {\r
8004   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8005   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
8006   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8007   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8008   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8009   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8010   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8011   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8012   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8013   { -1, -1 }\r
8014 };\r
8015 \r
8016 /* These modify either ncpEnables or gnuEnables */\r
8017 Enables cmailEnables[] = {\r
8018   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8019   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8020   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8021   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8022   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8023   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8024   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8025   { -1, -1 }\r
8026 };\r
8027 \r
8028 Enables machineThinkingEnables[] = {\r
8029   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8030   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8031   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8032   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8033   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8034   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8035   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8036   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8037   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8038   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8039   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8040   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8041   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8042 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8043   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8044   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8045   { -1, -1 }\r
8046 };\r
8047 \r
8048 Enables userThinkingEnables[] = {\r
8049   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8050   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8051   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8052   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8053   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8054   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8055   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8056   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8057   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8058   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8059   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8060   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8061   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8062 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8063   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8064   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8065   { -1, -1 }\r
8066 };\r
8067 \r
8068 /*---------------------------------------------------------------------------*\\r
8069  *\r
8070  *  Front-end interface functions exported by XBoard.\r
8071  *  Functions appear in same order as prototypes in frontend.h.\r
8072  * \r
8073 \*---------------------------------------------------------------------------*/\r
8074 VOID\r
8075 CheckMark(UINT item, int state)\r
8076 {\r
8077     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
8078 }\r
8079 \r
8080 VOID\r
8081 ModeHighlight()\r
8082 {\r
8083   static UINT prevChecked = 0;\r
8084   static int prevPausing = 0;\r
8085   UINT nowChecked;\r
8086 \r
8087   if (pausing != prevPausing) {\r
8088     prevPausing = pausing;\r
8089     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8090                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8091     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8092   }\r
8093 \r
8094   switch (gameMode) {\r
8095   case BeginningOfGame:\r
8096     if (appData.icsActive)\r
8097       nowChecked = IDM_IcsClient;\r
8098     else if (appData.noChessProgram)\r
8099       nowChecked = IDM_EditGame;\r
8100     else\r
8101       nowChecked = IDM_MachineBlack;\r
8102     break;\r
8103   case MachinePlaysBlack:\r
8104     nowChecked = IDM_MachineBlack;\r
8105     break;\r
8106   case MachinePlaysWhite:\r
8107     nowChecked = IDM_MachineWhite;\r
8108     break;\r
8109   case TwoMachinesPlay:\r
8110     nowChecked = IDM_TwoMachines;\r
8111     break;\r
8112   case AnalyzeMode:\r
8113     nowChecked = IDM_AnalysisMode;\r
8114     break;\r
8115   case AnalyzeFile:\r
8116     nowChecked = IDM_AnalyzeFile;\r
8117     break;\r
8118   case EditGame:\r
8119     nowChecked = IDM_EditGame;\r
8120     break;\r
8121   case PlayFromGameFile:\r
8122     nowChecked = IDM_LoadGame;\r
8123     break;\r
8124   case EditPosition:\r
8125     nowChecked = IDM_EditPosition;\r
8126     break;\r
8127   case Training:\r
8128     nowChecked = IDM_Training;\r
8129     break;\r
8130   case IcsPlayingWhite:\r
8131   case IcsPlayingBlack:\r
8132   case IcsObserving:\r
8133   case IcsIdle:\r
8134     nowChecked = IDM_IcsClient;\r
8135     break;\r
8136   default:\r
8137   case EndOfGame:\r
8138     nowChecked = 0;\r
8139     break;\r
8140   }\r
8141   CheckMark(prevChecked, MF_UNCHECKED);\r
8142   CheckMark(nowChecked, MF_CHECKED);\r
8143   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8144 \r
8145   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8146     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8147                           MF_BYCOMMAND|MF_ENABLED);\r
8148   } else {\r
8149     (void) EnableMenuItem(GetMenu(hwndMain), \r
8150                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8151   }\r
8152 \r
8153   prevChecked = nowChecked;\r
8154 \r
8155   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8156   if (appData.icsActive) {\r
8157        if (appData.icsEngineAnalyze) {\r
8158                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8159        } else {\r
8160                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8161        }\r
8162   }\r
8163   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8164 }\r
8165 \r
8166 VOID\r
8167 SetICSMode()\r
8168 {\r
8169   HMENU hmenu = GetMenu(hwndMain);\r
8170   SetMenuEnables(hmenu, icsEnables);\r
8171   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8172     MF_BYCOMMAND|MF_ENABLED);\r
8173 #if ZIPPY\r
8174   if (appData.zippyPlay) {\r
8175     SetMenuEnables(hmenu, zippyEnables);\r
8176     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8177          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8178           MF_BYCOMMAND|MF_ENABLED);\r
8179   }\r
8180 #endif\r
8181 }\r
8182 \r
8183 VOID\r
8184 SetGNUMode()\r
8185 {\r
8186   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8187 }\r
8188 \r
8189 VOID\r
8190 SetNCPMode()\r
8191 {\r
8192   HMENU hmenu = GetMenu(hwndMain);\r
8193   SetMenuEnables(hmenu, ncpEnables);\r
8194     DrawMenuBar(hwndMain);\r
8195 }\r
8196 \r
8197 VOID\r
8198 SetCmailMode()\r
8199 {\r
8200   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8201 }\r
8202 \r
8203 VOID \r
8204 SetTrainingModeOn()\r
8205 {\r
8206   int i;\r
8207   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8208   for (i = 0; i < N_BUTTONS; i++) {\r
8209     if (buttonDesc[i].hwnd != NULL)\r
8210       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8211   }\r
8212   CommentPopDown();\r
8213 }\r
8214 \r
8215 VOID SetTrainingModeOff()\r
8216 {\r
8217   int i;\r
8218   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8219   for (i = 0; i < N_BUTTONS; i++) {\r
8220     if (buttonDesc[i].hwnd != NULL)\r
8221       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8222   }\r
8223 }\r
8224 \r
8225 \r
8226 VOID\r
8227 SetUserThinkingEnables()\r
8228 {\r
8229   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8230 }\r
8231 \r
8232 VOID\r
8233 SetMachineThinkingEnables()\r
8234 {\r
8235   HMENU hMenu = GetMenu(hwndMain);\r
8236   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8237 \r
8238   SetMenuEnables(hMenu, machineThinkingEnables);\r
8239 \r
8240   if (gameMode == MachinePlaysBlack) {\r
8241     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8242   } else if (gameMode == MachinePlaysWhite) {\r
8243     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8244   } else if (gameMode == TwoMachinesPlay) {\r
8245     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8246   }\r
8247 }\r
8248 \r
8249 \r
8250 VOID\r
8251 DisplayTitle(char *str)\r
8252 {\r
8253   char title[MSG_SIZ], *host;\r
8254   if (str[0] != NULLCHAR) {\r
8255     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8256   } else if (appData.icsActive) {\r
8257     if (appData.icsCommPort[0] != NULLCHAR)\r
8258       host = "ICS";\r
8259     else \r
8260       host = appData.icsHost;\r
8261       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8262   } else if (appData.noChessProgram) {\r
8263     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8264   } else {\r
8265     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8266     strcat(title, ": ");\r
8267     strcat(title, first.tidy);\r
8268   }\r
8269   SetWindowText(hwndMain, title);\r
8270 }\r
8271 \r
8272 \r
8273 VOID\r
8274 DisplayMessage(char *str1, char *str2)\r
8275 {\r
8276   HDC hdc;\r
8277   HFONT oldFont;\r
8278   int remain = MESSAGE_TEXT_MAX - 1;\r
8279   int len;\r
8280 \r
8281   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8282   messageText[0] = NULLCHAR;\r
8283   if (*str1) {\r
8284     len = strlen(str1);\r
8285     if (len > remain) len = remain;\r
8286     strncpy(messageText, str1, len);\r
8287     messageText[len] = NULLCHAR;\r
8288     remain -= len;\r
8289   }\r
8290   if (*str2 && remain >= 2) {\r
8291     if (*str1) {\r
8292       strcat(messageText, "  ");\r
8293       remain -= 2;\r
8294     }\r
8295     len = strlen(str2);\r
8296     if (len > remain) len = remain;\r
8297     strncat(messageText, str2, len);\r
8298   }\r
8299   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8300   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8301 \r
8302   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8303 \r
8304   SAYMACHINEMOVE();\r
8305 \r
8306   hdc = GetDC(hwndMain);\r
8307   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8308   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8309              &messageRect, messageText, strlen(messageText), NULL);\r
8310   (void) SelectObject(hdc, oldFont);\r
8311   (void) ReleaseDC(hwndMain, hdc);\r
8312 }\r
8313 \r
8314 VOID\r
8315 DisplayError(char *str, int error)\r
8316 {\r
8317   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8318   int len;\r
8319 \r
8320   if (error == 0) {\r
8321     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8322   } else {\r
8323     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8324                         NULL, error, LANG_NEUTRAL,\r
8325                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8326     if (len > 0) {\r
8327       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8328     } else {\r
8329       ErrorMap *em = errmap;\r
8330       while (em->err != 0 && em->err != error) em++;\r
8331       if (em->err != 0) {\r
8332         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8333       } else {\r
8334         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8335       }\r
8336     }\r
8337   }\r
8338   \r
8339   ErrorPopUp(_("Error"), buf);\r
8340 }\r
8341 \r
8342 \r
8343 VOID\r
8344 DisplayMoveError(char *str)\r
8345 {\r
8346   fromX = fromY = -1;\r
8347   ClearHighlights();\r
8348   DrawPosition(FALSE, NULL);\r
8349   if (appData.popupMoveErrors) {\r
8350     ErrorPopUp(_("Error"), str);\r
8351   } else {\r
8352     DisplayMessage(str, "");\r
8353     moveErrorMessageUp = TRUE;\r
8354   }\r
8355 }\r
8356 \r
8357 VOID\r
8358 DisplayFatalError(char *str, int error, int exitStatus)\r
8359 {\r
8360   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8361   int len;\r
8362   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8363 \r
8364   if (error != 0) {\r
8365     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8366                         NULL, error, LANG_NEUTRAL,\r
8367                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8368     if (len > 0) {\r
8369       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8370     } else {\r
8371       ErrorMap *em = errmap;\r
8372       while (em->err != 0 && em->err != error) em++;\r
8373       if (em->err != 0) {\r
8374         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8375       } else {\r
8376         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8377       }\r
8378     }\r
8379     str = buf;\r
8380   }\r
8381   if (appData.debugMode) {\r
8382     fprintf(debugFP, "%s: %s\n", label, str);\r
8383   }\r
8384   if (appData.popupExitMessage) {\r
8385     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8386                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8387   }\r
8388   ExitEvent(exitStatus);\r
8389 }\r
8390 \r
8391 \r
8392 VOID\r
8393 DisplayInformation(char *str)\r
8394 {\r
8395   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8396 }\r
8397 \r
8398 \r
8399 VOID\r
8400 DisplayNote(char *str)\r
8401 {\r
8402   ErrorPopUp(_("Note"), str);\r
8403 }\r
8404 \r
8405 \r
8406 typedef struct {\r
8407   char *title, *question, *replyPrefix;\r
8408   ProcRef pr;\r
8409 } QuestionParams;\r
8410 \r
8411 LRESULT CALLBACK\r
8412 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8413 {\r
8414   static QuestionParams *qp;\r
8415   char reply[MSG_SIZ];\r
8416   int len, err;\r
8417 \r
8418   switch (message) {\r
8419   case WM_INITDIALOG:\r
8420     qp = (QuestionParams *) lParam;\r
8421     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8422     Translate(hDlg, DLG_Question);\r
8423     SetWindowText(hDlg, qp->title);\r
8424     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8425     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8426     return FALSE;\r
8427 \r
8428   case WM_COMMAND:\r
8429     switch (LOWORD(wParam)) {\r
8430     case IDOK:\r
8431       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8432       if (*reply) strcat(reply, " ");\r
8433       len = strlen(reply);\r
8434       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8435       strcat(reply, "\n");\r
8436       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8437       EndDialog(hDlg, TRUE);\r
8438       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8439       return TRUE;\r
8440     case IDCANCEL:\r
8441       EndDialog(hDlg, FALSE);\r
8442       return TRUE;\r
8443     default:\r
8444       break;\r
8445     }\r
8446     break;\r
8447   }\r
8448   return FALSE;\r
8449 }\r
8450 \r
8451 VOID\r
8452 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8453 {\r
8454     QuestionParams qp;\r
8455     FARPROC lpProc;\r
8456     \r
8457     qp.title = title;\r
8458     qp.question = question;\r
8459     qp.replyPrefix = replyPrefix;\r
8460     qp.pr = pr;\r
8461     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8462     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8463       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8464     FreeProcInstance(lpProc);\r
8465 }\r
8466 \r
8467 /* [AS] Pick FRC position */\r
8468 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8469 {\r
8470     static int * lpIndexFRC;\r
8471     BOOL index_is_ok;\r
8472     char buf[16];\r
8473 \r
8474     switch( message )\r
8475     {\r
8476     case WM_INITDIALOG:\r
8477         lpIndexFRC = (int *) lParam;\r
8478 \r
8479         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8480         Translate(hDlg, DLG_NewGameFRC);\r
8481 \r
8482         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8483         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8484         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8485         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8486 \r
8487         break;\r
8488 \r
8489     case WM_COMMAND:\r
8490         switch( LOWORD(wParam) ) {\r
8491         case IDOK:\r
8492             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8493             EndDialog( hDlg, 0 );\r
8494             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8495             return TRUE;\r
8496         case IDCANCEL:\r
8497             EndDialog( hDlg, 1 );   \r
8498             return TRUE;\r
8499         case IDC_NFG_Edit:\r
8500             if( HIWORD(wParam) == EN_CHANGE ) {\r
8501                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8502 \r
8503                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8504             }\r
8505             return TRUE;\r
8506         case IDC_NFG_Random:\r
8507           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8508             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8509             return TRUE;\r
8510         }\r
8511 \r
8512         break;\r
8513     }\r
8514 \r
8515     return FALSE;\r
8516 }\r
8517 \r
8518 int NewGameFRC()\r
8519 {\r
8520     int result;\r
8521     int index = appData.defaultFrcPosition;\r
8522     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8523 \r
8524     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8525 \r
8526     if( result == 0 ) {\r
8527         appData.defaultFrcPosition = index;\r
8528     }\r
8529 \r
8530     return result;\r
8531 }\r
8532 \r
8533 /* [AS] Game list options. Refactored by HGM */\r
8534 \r
8535 HWND gameListOptionsDialog;\r
8536 \r
8537 // low-level front-end: clear text edit / list widget\r
8538 void\r
8539 GLT_ClearList()\r
8540 {\r
8541     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8542 }\r
8543 \r
8544 // low-level front-end: clear text edit / list widget\r
8545 void\r
8546 GLT_DeSelectList()\r
8547 {\r
8548     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8549 }\r
8550 \r
8551 // low-level front-end: append line to text edit / list widget\r
8552 void\r
8553 GLT_AddToList( char *name )\r
8554 {\r
8555     if( name != 0 ) {\r
8556             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8557     }\r
8558 }\r
8559 \r
8560 // low-level front-end: get line from text edit / list widget\r
8561 Boolean\r
8562 GLT_GetFromList( int index, char *name )\r
8563 {\r
8564     if( name != 0 ) {\r
8565             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8566                 return TRUE;\r
8567     }\r
8568     return FALSE;\r
8569 }\r
8570 \r
8571 void GLT_MoveSelection( HWND hDlg, int delta )\r
8572 {\r
8573     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8574     int idx2 = idx1 + delta;\r
8575     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8576 \r
8577     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8578         char buf[128];\r
8579 \r
8580         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8581         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8582         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8583         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8584     }\r
8585 }\r
8586 \r
8587 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8588 {\r
8589     switch( message )\r
8590     {\r
8591     case WM_INITDIALOG:\r
8592         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8593         \r
8594         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8595         Translate(hDlg, DLG_GameListOptions);\r
8596 \r
8597         /* Initialize list */\r
8598         GLT_TagsToList( lpUserGLT );\r
8599 \r
8600         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8601 \r
8602         break;\r
8603 \r
8604     case WM_COMMAND:\r
8605         switch( LOWORD(wParam) ) {\r
8606         case IDOK:\r
8607             GLT_ParseList();\r
8608             EndDialog( hDlg, 0 );\r
8609             return TRUE;\r
8610         case IDCANCEL:\r
8611             EndDialog( hDlg, 1 );\r
8612             return TRUE;\r
8613 \r
8614         case IDC_GLT_Default:\r
8615             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8616             return TRUE;\r
8617 \r
8618         case IDC_GLT_Restore:\r
8619             GLT_TagsToList( appData.gameListTags );\r
8620             return TRUE;\r
8621 \r
8622         case IDC_GLT_Up:\r
8623             GLT_MoveSelection( hDlg, -1 );\r
8624             return TRUE;\r
8625 \r
8626         case IDC_GLT_Down:\r
8627             GLT_MoveSelection( hDlg, +1 );\r
8628             return TRUE;\r
8629         }\r
8630 \r
8631         break;\r
8632     }\r
8633 \r
8634     return FALSE;\r
8635 }\r
8636 \r
8637 int GameListOptions()\r
8638 {\r
8639     int result;\r
8640     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8641 \r
8642       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8643 \r
8644     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8645 \r
8646     if( result == 0 ) {\r
8647         /* [AS] Memory leak here! */\r
8648         appData.gameListTags = strdup( lpUserGLT ); \r
8649     }\r
8650 \r
8651     return result;\r
8652 }\r
8653 \r
8654 VOID\r
8655 DisplayIcsInteractionTitle(char *str)\r
8656 {\r
8657   char consoleTitle[MSG_SIZ];\r
8658 \r
8659     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8660     SetWindowText(hwndConsole, consoleTitle);\r
8661 \r
8662     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8663       char buf[MSG_SIZ], *p = buf, *q;\r
8664         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8665       do {\r
8666         q = strchr(p, ';');\r
8667         if(q) *q++ = 0;\r
8668         if(*p) ChatPopUp(p);\r
8669       } while(p=q);\r
8670     }\r
8671 \r
8672     SetActiveWindow(hwndMain);\r
8673 }\r
8674 \r
8675 void\r
8676 DrawPosition(int fullRedraw, Board board)\r
8677 {\r
8678   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8679 }\r
8680 \r
8681 void NotifyFrontendLogin()\r
8682 {\r
8683         if (hwndConsole)\r
8684                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8685 }\r
8686 \r
8687 VOID\r
8688 ResetFrontEnd()\r
8689 {\r
8690   fromX = fromY = -1;\r
8691   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8692     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8693     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8694     dragInfo.lastpos = dragInfo.pos;\r
8695     dragInfo.start.x = dragInfo.start.y = -1;\r
8696     dragInfo.from = dragInfo.start;\r
8697     ReleaseCapture();\r
8698     DrawPosition(TRUE, NULL);\r
8699   }\r
8700   TagsPopDown();\r
8701 }\r
8702 \r
8703 \r
8704 VOID\r
8705 CommentPopUp(char *title, char *str)\r
8706 {\r
8707   HWND hwnd = GetActiveWindow();\r
8708   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8709   SAY(str);\r
8710   SetActiveWindow(hwnd);\r
8711 }\r
8712 \r
8713 VOID\r
8714 CommentPopDown(void)\r
8715 {\r
8716   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8717   if (commentDialog) {\r
8718     ShowWindow(commentDialog, SW_HIDE);\r
8719   }\r
8720   commentUp = FALSE;\r
8721 }\r
8722 \r
8723 VOID\r
8724 EditCommentPopUp(int index, char *title, char *str)\r
8725 {\r
8726   EitherCommentPopUp(index, title, str, TRUE);\r
8727 }\r
8728 \r
8729 \r
8730 int\r
8731 Roar()\r
8732 {\r
8733   MyPlaySound(&sounds[(int)SoundRoar]);\r
8734   return 1;\r
8735 }\r
8736 \r
8737 VOID\r
8738 RingBell()\r
8739 {\r
8740   MyPlaySound(&sounds[(int)SoundMove]);\r
8741 }\r
8742 \r
8743 VOID PlayIcsWinSound()\r
8744 {\r
8745   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8746 }\r
8747 \r
8748 VOID PlayIcsLossSound()\r
8749 {\r
8750   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8751 }\r
8752 \r
8753 VOID PlayIcsDrawSound()\r
8754 {\r
8755   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8756 }\r
8757 \r
8758 VOID PlayIcsUnfinishedSound()\r
8759 {\r
8760   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8761 }\r
8762 \r
8763 VOID\r
8764 PlayAlarmSound()\r
8765 {\r
8766   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8767 }\r
8768 \r
8769 VOID\r
8770 PlayTellSound()\r
8771 {\r
8772   MyPlaySound(&textAttribs[ColorTell].sound);\r
8773 }\r
8774 \r
8775 \r
8776 VOID\r
8777 EchoOn()\r
8778 {\r
8779   HWND hInput;\r
8780   consoleEcho = TRUE;\r
8781   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8782   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8783   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8784 }\r
8785 \r
8786 \r
8787 VOID\r
8788 EchoOff()\r
8789 {\r
8790   CHARFORMAT cf;\r
8791   HWND hInput;\r
8792   consoleEcho = FALSE;\r
8793   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8794   /* This works OK: set text and background both to the same color */\r
8795   cf = consoleCF;\r
8796   cf.crTextColor = COLOR_ECHOOFF;\r
8797   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8798   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8799 }\r
8800 \r
8801 /* No Raw()...? */\r
8802 \r
8803 void Colorize(ColorClass cc, int continuation)\r
8804 {\r
8805   currentColorClass = cc;\r
8806   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8807   consoleCF.crTextColor = textAttribs[cc].color;\r
8808   consoleCF.dwEffects = textAttribs[cc].effects;\r
8809   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8810 }\r
8811 \r
8812 char *\r
8813 UserName()\r
8814 {\r
8815   static char buf[MSG_SIZ];\r
8816   DWORD bufsiz = MSG_SIZ;\r
8817 \r
8818   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8819         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8820   }\r
8821   if (!GetUserName(buf, &bufsiz)) {\r
8822     /*DisplayError("Error getting user name", GetLastError());*/\r
8823     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8824   }\r
8825   return buf;\r
8826 }\r
8827 \r
8828 char *\r
8829 HostName()\r
8830 {\r
8831   static char buf[MSG_SIZ];\r
8832   DWORD bufsiz = MSG_SIZ;\r
8833 \r
8834   if (!GetComputerName(buf, &bufsiz)) {\r
8835     /*DisplayError("Error getting host name", GetLastError());*/\r
8836     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8837   }\r
8838   return buf;\r
8839 }\r
8840 \r
8841 \r
8842 int\r
8843 ClockTimerRunning()\r
8844 {\r
8845   return clockTimerEvent != 0;\r
8846 }\r
8847 \r
8848 int\r
8849 StopClockTimer()\r
8850 {\r
8851   if (clockTimerEvent == 0) return FALSE;\r
8852   KillTimer(hwndMain, clockTimerEvent);\r
8853   clockTimerEvent = 0;\r
8854   return TRUE;\r
8855 }\r
8856 \r
8857 void\r
8858 StartClockTimer(long millisec)\r
8859 {\r
8860   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8861                              (UINT) millisec, NULL);\r
8862 }\r
8863 \r
8864 void\r
8865 DisplayWhiteClock(long timeRemaining, int highlight)\r
8866 {\r
8867   HDC hdc;\r
8868   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8869 \r
8870   if(appData.noGUI) return;\r
8871   hdc = GetDC(hwndMain);\r
8872   if (!IsIconic(hwndMain)) {\r
8873     DisplayAClock(hdc, timeRemaining, highlight, \r
8874                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8875   }\r
8876   if (highlight && iconCurrent == iconBlack) {\r
8877     iconCurrent = iconWhite;\r
8878     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8879     if (IsIconic(hwndMain)) {\r
8880       DrawIcon(hdc, 2, 2, iconCurrent);\r
8881     }\r
8882   }\r
8883   (void) ReleaseDC(hwndMain, hdc);\r
8884   if (hwndConsole)\r
8885     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8886 }\r
8887 \r
8888 void\r
8889 DisplayBlackClock(long timeRemaining, int highlight)\r
8890 {\r
8891   HDC hdc;\r
8892   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8893 \r
8894 \r
8895   if(appData.noGUI) return;\r
8896   hdc = GetDC(hwndMain);\r
8897   if (!IsIconic(hwndMain)) {\r
8898     DisplayAClock(hdc, timeRemaining, highlight, \r
8899                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8900   }\r
8901   if (highlight && iconCurrent == iconWhite) {\r
8902     iconCurrent = iconBlack;\r
8903     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8904     if (IsIconic(hwndMain)) {\r
8905       DrawIcon(hdc, 2, 2, iconCurrent);\r
8906     }\r
8907   }\r
8908   (void) ReleaseDC(hwndMain, hdc);\r
8909   if (hwndConsole)\r
8910     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8911 }\r
8912 \r
8913 \r
8914 int\r
8915 LoadGameTimerRunning()\r
8916 {\r
8917   return loadGameTimerEvent != 0;\r
8918 }\r
8919 \r
8920 int\r
8921 StopLoadGameTimer()\r
8922 {\r
8923   if (loadGameTimerEvent == 0) return FALSE;\r
8924   KillTimer(hwndMain, loadGameTimerEvent);\r
8925   loadGameTimerEvent = 0;\r
8926   return TRUE;\r
8927 }\r
8928 \r
8929 void\r
8930 StartLoadGameTimer(long millisec)\r
8931 {\r
8932   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8933                                 (UINT) millisec, NULL);\r
8934 }\r
8935 \r
8936 void\r
8937 AutoSaveGame()\r
8938 {\r
8939   char *defName;\r
8940   FILE *f;\r
8941   char fileTitle[MSG_SIZ];\r
8942 \r
8943   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8944   f = OpenFileDialog(hwndMain, "a", defName,\r
8945                      appData.oldSaveStyle ? "gam" : "pgn",\r
8946                      GAME_FILT, \r
8947                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8948   if (f != NULL) {\r
8949     SaveGame(f, 0, "");\r
8950     fclose(f);\r
8951   }\r
8952 }\r
8953 \r
8954 \r
8955 void\r
8956 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8957 {\r
8958   if (delayedTimerEvent != 0) {\r
8959     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8960       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8961     }\r
8962     KillTimer(hwndMain, delayedTimerEvent);\r
8963     delayedTimerEvent = 0;\r
8964     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8965     delayedTimerCallback();\r
8966   }\r
8967   delayedTimerCallback = cb;\r
8968   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8969                                 (UINT) millisec, NULL);\r
8970 }\r
8971 \r
8972 DelayedEventCallback\r
8973 GetDelayedEvent()\r
8974 {\r
8975   if (delayedTimerEvent) {\r
8976     return delayedTimerCallback;\r
8977   } else {\r
8978     return NULL;\r
8979   }\r
8980 }\r
8981 \r
8982 void\r
8983 CancelDelayedEvent()\r
8984 {\r
8985   if (delayedTimerEvent) {\r
8986     KillTimer(hwndMain, delayedTimerEvent);\r
8987     delayedTimerEvent = 0;\r
8988   }\r
8989 }\r
8990 \r
8991 DWORD GetWin32Priority(int nice)\r
8992 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8993 /*\r
8994 REALTIME_PRIORITY_CLASS     0x00000100\r
8995 HIGH_PRIORITY_CLASS         0x00000080\r
8996 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8997 NORMAL_PRIORITY_CLASS       0x00000020\r
8998 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8999 IDLE_PRIORITY_CLASS         0x00000040\r
9000 */\r
9001         if (nice < -15) return 0x00000080;\r
9002         if (nice < 0)   return 0x00008000;\r
9003         if (nice == 0)  return 0x00000020;\r
9004         if (nice < 15)  return 0x00004000;\r
9005         return 0x00000040;\r
9006 }\r
9007 \r
9008 void RunCommand(char *cmdLine)\r
9009 {\r
9010   /* Now create the child process. */\r
9011   STARTUPINFO siStartInfo;\r
9012   PROCESS_INFORMATION piProcInfo;\r
9013 \r
9014   siStartInfo.cb = sizeof(STARTUPINFO);\r
9015   siStartInfo.lpReserved = NULL;\r
9016   siStartInfo.lpDesktop = NULL;\r
9017   siStartInfo.lpTitle = NULL;\r
9018   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9019   siStartInfo.cbReserved2 = 0;\r
9020   siStartInfo.lpReserved2 = NULL;\r
9021   siStartInfo.hStdInput = NULL;\r
9022   siStartInfo.hStdOutput = NULL;\r
9023   siStartInfo.hStdError = NULL;\r
9024 \r
9025   CreateProcess(NULL,\r
9026                 cmdLine,           /* command line */\r
9027                 NULL,      /* process security attributes */\r
9028                 NULL,      /* primary thread security attrs */\r
9029                 TRUE,      /* handles are inherited */\r
9030                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9031                 NULL,      /* use parent's environment */\r
9032                 NULL,\r
9033                 &siStartInfo, /* STARTUPINFO pointer */\r
9034                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
9035 \r
9036   CloseHandle(piProcInfo.hThread);\r
9037 }\r
9038 \r
9039 /* Start a child process running the given program.\r
9040    The process's standard output can be read from "from", and its\r
9041    standard input can be written to "to".\r
9042    Exit with fatal error if anything goes wrong.\r
9043    Returns an opaque pointer that can be used to destroy the process\r
9044    later.\r
9045 */\r
9046 int\r
9047 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9048 {\r
9049 #define BUFSIZE 4096\r
9050 \r
9051   HANDLE hChildStdinRd, hChildStdinWr,\r
9052     hChildStdoutRd, hChildStdoutWr;\r
9053   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9054   SECURITY_ATTRIBUTES saAttr;\r
9055   BOOL fSuccess;\r
9056   PROCESS_INFORMATION piProcInfo;\r
9057   STARTUPINFO siStartInfo;\r
9058   ChildProc *cp;\r
9059   char buf[MSG_SIZ];\r
9060   DWORD err;\r
9061 \r
9062   if (appData.debugMode) {\r
9063     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9064   }\r
9065 \r
9066   *pr = NoProc;\r
9067 \r
9068   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9069   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9070   saAttr.bInheritHandle = TRUE;\r
9071   saAttr.lpSecurityDescriptor = NULL;\r
9072 \r
9073   /*\r
9074    * The steps for redirecting child's STDOUT:\r
9075    *     1. Create anonymous pipe to be STDOUT for child.\r
9076    *     2. Create a noninheritable duplicate of read handle,\r
9077    *         and close the inheritable read handle.\r
9078    */\r
9079 \r
9080   /* Create a pipe for the child's STDOUT. */\r
9081   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9082     return GetLastError();\r
9083   }\r
9084 \r
9085   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9086   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9087                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9088                              FALSE,     /* not inherited */\r
9089                              DUPLICATE_SAME_ACCESS);\r
9090   if (! fSuccess) {\r
9091     return GetLastError();\r
9092   }\r
9093   CloseHandle(hChildStdoutRd);\r
9094 \r
9095   /*\r
9096    * The steps for redirecting child's STDIN:\r
9097    *     1. Create anonymous pipe to be STDIN for child.\r
9098    *     2. Create a noninheritable duplicate of write handle,\r
9099    *         and close the inheritable write handle.\r
9100    */\r
9101 \r
9102   /* Create a pipe for the child's STDIN. */\r
9103   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9104     return GetLastError();\r
9105   }\r
9106 \r
9107   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9108   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9109                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9110                              FALSE,     /* not inherited */\r
9111                              DUPLICATE_SAME_ACCESS);\r
9112   if (! fSuccess) {\r
9113     return GetLastError();\r
9114   }\r
9115   CloseHandle(hChildStdinWr);\r
9116 \r
9117   /* Arrange to (1) look in dir for the child .exe file, and\r
9118    * (2) have dir be the child's working directory.  Interpret\r
9119    * dir relative to the directory WinBoard loaded from. */\r
9120   GetCurrentDirectory(MSG_SIZ, buf);\r
9121   SetCurrentDirectory(installDir);\r
9122   SetCurrentDirectory(dir);\r
9123 \r
9124   /* Now create the child process. */\r
9125 \r
9126   siStartInfo.cb = sizeof(STARTUPINFO);\r
9127   siStartInfo.lpReserved = NULL;\r
9128   siStartInfo.lpDesktop = NULL;\r
9129   siStartInfo.lpTitle = NULL;\r
9130   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9131   siStartInfo.cbReserved2 = 0;\r
9132   siStartInfo.lpReserved2 = NULL;\r
9133   siStartInfo.hStdInput = hChildStdinRd;\r
9134   siStartInfo.hStdOutput = hChildStdoutWr;\r
9135   siStartInfo.hStdError = hChildStdoutWr;\r
9136 \r
9137   fSuccess = CreateProcess(NULL,\r
9138                            cmdLine,        /* command line */\r
9139                            NULL,           /* process security attributes */\r
9140                            NULL,           /* primary thread security attrs */\r
9141                            TRUE,           /* handles are inherited */\r
9142                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9143                            NULL,           /* use parent's environment */\r
9144                            NULL,\r
9145                            &siStartInfo, /* STARTUPINFO pointer */\r
9146                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9147 \r
9148   err = GetLastError();\r
9149   SetCurrentDirectory(buf); /* return to prev directory */\r
9150   if (! fSuccess) {\r
9151     return err;\r
9152   }\r
9153 \r
9154   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9155     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9156     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9157   }\r
9158 \r
9159   /* Close the handles we don't need in the parent */\r
9160   CloseHandle(piProcInfo.hThread);\r
9161   CloseHandle(hChildStdinRd);\r
9162   CloseHandle(hChildStdoutWr);\r
9163 \r
9164   /* Prepare return value */\r
9165   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9166   cp->kind = CPReal;\r
9167   cp->hProcess = piProcInfo.hProcess;\r
9168   cp->pid = piProcInfo.dwProcessId;\r
9169   cp->hFrom = hChildStdoutRdDup;\r
9170   cp->hTo = hChildStdinWrDup;\r
9171 \r
9172   *pr = (void *) cp;\r
9173 \r
9174   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9175      2000 where engines sometimes don't see the initial command(s)\r
9176      from WinBoard and hang.  I don't understand how that can happen,\r
9177      but the Sleep is harmless, so I've put it in.  Others have also\r
9178      reported what may be the same problem, so hopefully this will fix\r
9179      it for them too.  */\r
9180   Sleep(500);\r
9181 \r
9182   return NO_ERROR;\r
9183 }\r
9184 \r
9185 \r
9186 void\r
9187 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9188 {\r
9189   ChildProc *cp; int result;\r
9190 \r
9191   cp = (ChildProc *) pr;\r
9192   if (cp == NULL) return;\r
9193 \r
9194   switch (cp->kind) {\r
9195   case CPReal:\r
9196     /* TerminateProcess is considered harmful, so... */\r
9197     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9198     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9199     /* The following doesn't work because the chess program\r
9200        doesn't "have the same console" as WinBoard.  Maybe\r
9201        we could arrange for this even though neither WinBoard\r
9202        nor the chess program uses a console for stdio? */\r
9203     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9204 \r
9205     /* [AS] Special termination modes for misbehaving programs... */\r
9206     if( signal == 9 ) { \r
9207         result = TerminateProcess( cp->hProcess, 0 );\r
9208 \r
9209         if ( appData.debugMode) {\r
9210             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9211         }\r
9212     }\r
9213     else if( signal == 10 ) {\r
9214         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9215 \r
9216         if( dw != WAIT_OBJECT_0 ) {\r
9217             result = TerminateProcess( cp->hProcess, 0 );\r
9218 \r
9219             if ( appData.debugMode) {\r
9220                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9221             }\r
9222 \r
9223         }\r
9224     }\r
9225 \r
9226     CloseHandle(cp->hProcess);\r
9227     break;\r
9228 \r
9229   case CPComm:\r
9230     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9231     break;\r
9232 \r
9233   case CPSock:\r
9234     closesocket(cp->sock);\r
9235     WSACleanup();\r
9236     break;\r
9237 \r
9238   case CPRcmd:\r
9239     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9240     closesocket(cp->sock);\r
9241     closesocket(cp->sock2);\r
9242     WSACleanup();\r
9243     break;\r
9244   }\r
9245   free(cp);\r
9246 }\r
9247 \r
9248 void\r
9249 InterruptChildProcess(ProcRef pr)\r
9250 {\r
9251   ChildProc *cp;\r
9252 \r
9253   cp = (ChildProc *) pr;\r
9254   if (cp == NULL) return;\r
9255   switch (cp->kind) {\r
9256   case CPReal:\r
9257     /* The following doesn't work because the chess program\r
9258        doesn't "have the same console" as WinBoard.  Maybe\r
9259        we could arrange for this even though neither WinBoard\r
9260        nor the chess program uses a console for stdio */\r
9261     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9262     break;\r
9263 \r
9264   case CPComm:\r
9265   case CPSock:\r
9266     /* Can't interrupt */\r
9267     break;\r
9268 \r
9269   case CPRcmd:\r
9270     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9271     break;\r
9272   }\r
9273 }\r
9274 \r
9275 \r
9276 int\r
9277 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9278 {\r
9279   char cmdLine[MSG_SIZ];\r
9280 \r
9281   if (port[0] == NULLCHAR) {\r
9282     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9283   } else {\r
9284     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9285   }\r
9286   return StartChildProcess(cmdLine, "", pr);\r
9287 }\r
9288 \r
9289 \r
9290 /* Code to open TCP sockets */\r
9291 \r
9292 int\r
9293 OpenTCP(char *host, char *port, ProcRef *pr)\r
9294 {\r
9295   ChildProc *cp;\r
9296   int err;\r
9297   SOCKET s;\r
9298 \r
9299   struct sockaddr_in sa, mysa;\r
9300   struct hostent FAR *hp;\r
9301   unsigned short uport;\r
9302   WORD wVersionRequested;\r
9303   WSADATA wsaData;\r
9304 \r
9305   /* Initialize socket DLL */\r
9306   wVersionRequested = MAKEWORD(1, 1);\r
9307   err = WSAStartup(wVersionRequested, &wsaData);\r
9308   if (err != 0) return err;\r
9309 \r
9310   /* Make socket */\r
9311   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9312     err = WSAGetLastError();\r
9313     WSACleanup();\r
9314     return err;\r
9315   }\r
9316 \r
9317   /* Bind local address using (mostly) don't-care values.\r
9318    */\r
9319   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9320   mysa.sin_family = AF_INET;\r
9321   mysa.sin_addr.s_addr = INADDR_ANY;\r
9322   uport = (unsigned short) 0;\r
9323   mysa.sin_port = htons(uport);\r
9324   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9325       == SOCKET_ERROR) {\r
9326     err = WSAGetLastError();\r
9327     WSACleanup();\r
9328     return err;\r
9329   }\r
9330 \r
9331   /* Resolve remote host name */\r
9332   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9333   if (!(hp = gethostbyname(host))) {\r
9334     unsigned int b0, b1, b2, b3;\r
9335 \r
9336     err = WSAGetLastError();\r
9337 \r
9338     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9339       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9340       hp->h_addrtype = AF_INET;\r
9341       hp->h_length = 4;\r
9342       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9343       hp->h_addr_list[0] = (char *) malloc(4);\r
9344       hp->h_addr_list[0][0] = (char) b0;\r
9345       hp->h_addr_list[0][1] = (char) b1;\r
9346       hp->h_addr_list[0][2] = (char) b2;\r
9347       hp->h_addr_list[0][3] = (char) b3;\r
9348     } else {\r
9349       WSACleanup();\r
9350       return err;\r
9351     }\r
9352   }\r
9353   sa.sin_family = hp->h_addrtype;\r
9354   uport = (unsigned short) atoi(port);\r
9355   sa.sin_port = htons(uport);\r
9356   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9357 \r
9358   /* Make connection */\r
9359   if (connect(s, (struct sockaddr *) &sa,\r
9360               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9361     err = WSAGetLastError();\r
9362     WSACleanup();\r
9363     return err;\r
9364   }\r
9365 \r
9366   /* Prepare return value */\r
9367   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9368   cp->kind = CPSock;\r
9369   cp->sock = s;\r
9370   *pr = (ProcRef *) cp;\r
9371 \r
9372   return NO_ERROR;\r
9373 }\r
9374 \r
9375 int\r
9376 OpenCommPort(char *name, ProcRef *pr)\r
9377 {\r
9378   HANDLE h;\r
9379   COMMTIMEOUTS ct;\r
9380   ChildProc *cp;\r
9381   char fullname[MSG_SIZ];\r
9382 \r
9383   if (*name != '\\')\r
9384     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9385   else\r
9386     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9387 \r
9388   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9389                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9390   if (h == (HANDLE) -1) {\r
9391     return GetLastError();\r
9392   }\r
9393   hCommPort = h;\r
9394 \r
9395   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9396 \r
9397   /* Accumulate characters until a 100ms pause, then parse */\r
9398   ct.ReadIntervalTimeout = 100;\r
9399   ct.ReadTotalTimeoutMultiplier = 0;\r
9400   ct.ReadTotalTimeoutConstant = 0;\r
9401   ct.WriteTotalTimeoutMultiplier = 0;\r
9402   ct.WriteTotalTimeoutConstant = 0;\r
9403   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9404 \r
9405   /* Prepare return value */\r
9406   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9407   cp->kind = CPComm;\r
9408   cp->hFrom = h;\r
9409   cp->hTo = h;\r
9410   *pr = (ProcRef *) cp;\r
9411 \r
9412   return NO_ERROR;\r
9413 }\r
9414 \r
9415 int\r
9416 OpenLoopback(ProcRef *pr)\r
9417 {\r
9418   DisplayFatalError(_("Not implemented"), 0, 1);\r
9419   return NO_ERROR;\r
9420 }\r
9421 \r
9422 \r
9423 int\r
9424 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9425 {\r
9426   ChildProc *cp;\r
9427   int err;\r
9428   SOCKET s, s2, s3;\r
9429   struct sockaddr_in sa, mysa;\r
9430   struct hostent FAR *hp;\r
9431   unsigned short uport;\r
9432   WORD wVersionRequested;\r
9433   WSADATA wsaData;\r
9434   int fromPort;\r
9435   char stderrPortStr[MSG_SIZ];\r
9436 \r
9437   /* Initialize socket DLL */\r
9438   wVersionRequested = MAKEWORD(1, 1);\r
9439   err = WSAStartup(wVersionRequested, &wsaData);\r
9440   if (err != 0) return err;\r
9441 \r
9442   /* Resolve remote host name */\r
9443   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9444   if (!(hp = gethostbyname(host))) {\r
9445     unsigned int b0, b1, b2, b3;\r
9446 \r
9447     err = WSAGetLastError();\r
9448 \r
9449     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9450       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9451       hp->h_addrtype = AF_INET;\r
9452       hp->h_length = 4;\r
9453       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9454       hp->h_addr_list[0] = (char *) malloc(4);\r
9455       hp->h_addr_list[0][0] = (char) b0;\r
9456       hp->h_addr_list[0][1] = (char) b1;\r
9457       hp->h_addr_list[0][2] = (char) b2;\r
9458       hp->h_addr_list[0][3] = (char) b3;\r
9459     } else {\r
9460       WSACleanup();\r
9461       return err;\r
9462     }\r
9463   }\r
9464   sa.sin_family = hp->h_addrtype;\r
9465   uport = (unsigned short) 514;\r
9466   sa.sin_port = htons(uport);\r
9467   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9468 \r
9469   /* Bind local socket to unused "privileged" port address\r
9470    */\r
9471   s = INVALID_SOCKET;\r
9472   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9473   mysa.sin_family = AF_INET;\r
9474   mysa.sin_addr.s_addr = INADDR_ANY;\r
9475   for (fromPort = 1023;; fromPort--) {\r
9476     if (fromPort < 0) {\r
9477       WSACleanup();\r
9478       return WSAEADDRINUSE;\r
9479     }\r
9480     if (s == INVALID_SOCKET) {\r
9481       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9482         err = WSAGetLastError();\r
9483         WSACleanup();\r
9484         return err;\r
9485       }\r
9486     }\r
9487     uport = (unsigned short) fromPort;\r
9488     mysa.sin_port = htons(uport);\r
9489     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9490         == SOCKET_ERROR) {\r
9491       err = WSAGetLastError();\r
9492       if (err == WSAEADDRINUSE) continue;\r
9493       WSACleanup();\r
9494       return err;\r
9495     }\r
9496     if (connect(s, (struct sockaddr *) &sa,\r
9497       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9498       err = WSAGetLastError();\r
9499       if (err == WSAEADDRINUSE) {\r
9500         closesocket(s);\r
9501         s = -1;\r
9502         continue;\r
9503       }\r
9504       WSACleanup();\r
9505       return err;\r
9506     }\r
9507     break;\r
9508   }\r
9509 \r
9510   /* Bind stderr local socket to unused "privileged" port address\r
9511    */\r
9512   s2 = INVALID_SOCKET;\r
9513   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9514   mysa.sin_family = AF_INET;\r
9515   mysa.sin_addr.s_addr = INADDR_ANY;\r
9516   for (fromPort = 1023;; fromPort--) {\r
9517     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9518     if (fromPort < 0) {\r
9519       (void) closesocket(s);\r
9520       WSACleanup();\r
9521       return WSAEADDRINUSE;\r
9522     }\r
9523     if (s2 == INVALID_SOCKET) {\r
9524       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9525         err = WSAGetLastError();\r
9526         closesocket(s);\r
9527         WSACleanup();\r
9528         return err;\r
9529       }\r
9530     }\r
9531     uport = (unsigned short) fromPort;\r
9532     mysa.sin_port = htons(uport);\r
9533     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9534         == SOCKET_ERROR) {\r
9535       err = WSAGetLastError();\r
9536       if (err == WSAEADDRINUSE) continue;\r
9537       (void) closesocket(s);\r
9538       WSACleanup();\r
9539       return err;\r
9540     }\r
9541     if (listen(s2, 1) == SOCKET_ERROR) {\r
9542       err = WSAGetLastError();\r
9543       if (err == WSAEADDRINUSE) {\r
9544         closesocket(s2);\r
9545         s2 = INVALID_SOCKET;\r
9546         continue;\r
9547       }\r
9548       (void) closesocket(s);\r
9549       (void) closesocket(s2);\r
9550       WSACleanup();\r
9551       return err;\r
9552     }\r
9553     break;\r
9554   }\r
9555   prevStderrPort = fromPort; // remember port used\r
9556   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9557 \r
9558   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9559     err = WSAGetLastError();\r
9560     (void) closesocket(s);\r
9561     (void) closesocket(s2);\r
9562     WSACleanup();\r
9563     return err;\r
9564   }\r
9565 \r
9566   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9567     err = WSAGetLastError();\r
9568     (void) closesocket(s);\r
9569     (void) closesocket(s2);\r
9570     WSACleanup();\r
9571     return err;\r
9572   }\r
9573   if (*user == NULLCHAR) user = UserName();\r
9574   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9575     err = WSAGetLastError();\r
9576     (void) closesocket(s);\r
9577     (void) closesocket(s2);\r
9578     WSACleanup();\r
9579     return err;\r
9580   }\r
9581   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9582     err = WSAGetLastError();\r
9583     (void) closesocket(s);\r
9584     (void) closesocket(s2);\r
9585     WSACleanup();\r
9586     return err;\r
9587   }\r
9588 \r
9589   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9590     err = WSAGetLastError();\r
9591     (void) closesocket(s);\r
9592     (void) closesocket(s2);\r
9593     WSACleanup();\r
9594     return err;\r
9595   }\r
9596   (void) closesocket(s2);  /* Stop listening */\r
9597 \r
9598   /* Prepare return value */\r
9599   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9600   cp->kind = CPRcmd;\r
9601   cp->sock = s;\r
9602   cp->sock2 = s3;\r
9603   *pr = (ProcRef *) cp;\r
9604 \r
9605   return NO_ERROR;\r
9606 }\r
9607 \r
9608 \r
9609 InputSourceRef\r
9610 AddInputSource(ProcRef pr, int lineByLine,\r
9611                InputCallback func, VOIDSTAR closure)\r
9612 {\r
9613   InputSource *is, *is2 = NULL;\r
9614   ChildProc *cp = (ChildProc *) pr;\r
9615 \r
9616   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9617   is->lineByLine = lineByLine;\r
9618   is->func = func;\r
9619   is->closure = closure;\r
9620   is->second = NULL;\r
9621   is->next = is->buf;\r
9622   if (pr == NoProc) {\r
9623     is->kind = CPReal;\r
9624     consoleInputSource = is;\r
9625   } else {\r
9626     is->kind = cp->kind;\r
9627     /* \r
9628         [AS] Try to avoid a race condition if the thread is given control too early:\r
9629         we create all threads suspended so that the is->hThread variable can be\r
9630         safely assigned, then let the threads start with ResumeThread.\r
9631     */\r
9632     switch (cp->kind) {\r
9633     case CPReal:\r
9634       is->hFile = cp->hFrom;\r
9635       cp->hFrom = NULL; /* now owned by InputThread */\r
9636       is->hThread =\r
9637         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9638                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9639       break;\r
9640 \r
9641     case CPComm:\r
9642       is->hFile = cp->hFrom;\r
9643       cp->hFrom = NULL; /* now owned by InputThread */\r
9644       is->hThread =\r
9645         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9646                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9647       break;\r
9648 \r
9649     case CPSock:\r
9650       is->sock = cp->sock;\r
9651       is->hThread =\r
9652         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9653                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9654       break;\r
9655 \r
9656     case CPRcmd:\r
9657       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9658       *is2 = *is;\r
9659       is->sock = cp->sock;\r
9660       is->second = is2;\r
9661       is2->sock = cp->sock2;\r
9662       is2->second = is2;\r
9663       is->hThread =\r
9664         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9665                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9666       is2->hThread =\r
9667         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9668                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9669       break;\r
9670     }\r
9671 \r
9672     if( is->hThread != NULL ) {\r
9673         ResumeThread( is->hThread );\r
9674     }\r
9675 \r
9676     if( is2 != NULL && is2->hThread != NULL ) {\r
9677         ResumeThread( is2->hThread );\r
9678     }\r
9679   }\r
9680 \r
9681   return (InputSourceRef) is;\r
9682 }\r
9683 \r
9684 void\r
9685 RemoveInputSource(InputSourceRef isr)\r
9686 {\r
9687   InputSource *is;\r
9688 \r
9689   is = (InputSource *) isr;\r
9690   is->hThread = NULL;  /* tell thread to stop */\r
9691   CloseHandle(is->hThread);\r
9692   if (is->second != NULL) {\r
9693     is->second->hThread = NULL;\r
9694     CloseHandle(is->second->hThread);\r
9695   }\r
9696 }\r
9697 \r
9698 int no_wrap(char *message, int count)\r
9699 {\r
9700     ConsoleOutput(message, count, FALSE);\r
9701     return count;\r
9702 }\r
9703 \r
9704 int\r
9705 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9706 {\r
9707   DWORD dOutCount;\r
9708   int outCount = SOCKET_ERROR;\r
9709   ChildProc *cp = (ChildProc *) pr;\r
9710   static OVERLAPPED ovl;\r
9711   static int line = 0;\r
9712 \r
9713   if (pr == NoProc)\r
9714   {\r
9715     if (appData.noJoin || !appData.useInternalWrap)\r
9716       return no_wrap(message, count);\r
9717     else\r
9718     {\r
9719       int width = get_term_width();\r
9720       int len = wrap(NULL, message, count, width, &line);\r
9721       char *msg = malloc(len);\r
9722       int dbgchk;\r
9723 \r
9724       if (!msg)\r
9725         return no_wrap(message, count);\r
9726       else\r
9727       {\r
9728         dbgchk = wrap(msg, message, count, width, &line);\r
9729         if (dbgchk != len && appData.debugMode)\r
9730             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9731         ConsoleOutput(msg, len, FALSE);\r
9732         free(msg);\r
9733         return len;\r
9734       }\r
9735     }\r
9736   }\r
9737 \r
9738   if (ovl.hEvent == NULL) {\r
9739     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9740   }\r
9741   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9742 \r
9743   switch (cp->kind) {\r
9744   case CPSock:\r
9745   case CPRcmd:\r
9746     outCount = send(cp->sock, message, count, 0);\r
9747     if (outCount == SOCKET_ERROR) {\r
9748       *outError = WSAGetLastError();\r
9749     } else {\r
9750       *outError = NO_ERROR;\r
9751     }\r
9752     break;\r
9753 \r
9754   case CPReal:\r
9755     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9756                   &dOutCount, NULL)) {\r
9757       *outError = NO_ERROR;\r
9758       outCount = (int) dOutCount;\r
9759     } else {\r
9760       *outError = GetLastError();\r
9761     }\r
9762     break;\r
9763 \r
9764   case CPComm:\r
9765     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9766                             &dOutCount, &ovl);\r
9767     if (*outError == NO_ERROR) {\r
9768       outCount = (int) dOutCount;\r
9769     }\r
9770     break;\r
9771   }\r
9772   return outCount;\r
9773 }\r
9774 \r
9775 void\r
9776 DoSleep(int n)\r
9777 {\r
9778     if(n != 0) Sleep(n);\r
9779 }\r
9780 \r
9781 int\r
9782 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9783                        long msdelay)\r
9784 {\r
9785   /* Ignore delay, not implemented for WinBoard */\r
9786   return OutputToProcess(pr, message, count, outError);\r
9787 }\r
9788 \r
9789 \r
9790 void\r
9791 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9792                         char *buf, int count, int error)\r
9793 {\r
9794   DisplayFatalError(_("Not implemented"), 0, 1);\r
9795 }\r
9796 \r
9797 /* see wgamelist.c for Game List functions */\r
9798 /* see wedittags.c for Edit Tags functions */\r
9799 \r
9800 \r
9801 int\r
9802 ICSInitScript()\r
9803 {\r
9804   FILE *f;\r
9805   char buf[MSG_SIZ];\r
9806   char *dummy;\r
9807 \r
9808   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9809     f = fopen(buf, "r");\r
9810     if (f != NULL) {\r
9811       ProcessICSInitScript(f);\r
9812       fclose(f);\r
9813       return TRUE;\r
9814     }\r
9815   }\r
9816   return FALSE;\r
9817 }\r
9818 \r
9819 \r
9820 VOID\r
9821 StartAnalysisClock()\r
9822 {\r
9823   if (analysisTimerEvent) return;\r
9824   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9825                                         (UINT) 2000, NULL);\r
9826 }\r
9827 \r
9828 VOID\r
9829 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9830 {\r
9831   highlightInfo.sq[0].x = fromX;\r
9832   highlightInfo.sq[0].y = fromY;\r
9833   highlightInfo.sq[1].x = toX;\r
9834   highlightInfo.sq[1].y = toY;\r
9835 }\r
9836 \r
9837 VOID\r
9838 ClearHighlights()\r
9839 {\r
9840   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9841     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9842 }\r
9843 \r
9844 VOID\r
9845 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9846 {\r
9847   premoveHighlightInfo.sq[0].x = fromX;\r
9848   premoveHighlightInfo.sq[0].y = fromY;\r
9849   premoveHighlightInfo.sq[1].x = toX;\r
9850   premoveHighlightInfo.sq[1].y = toY;\r
9851 }\r
9852 \r
9853 VOID\r
9854 ClearPremoveHighlights()\r
9855 {\r
9856   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9857     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9858 }\r
9859 \r
9860 VOID\r
9861 ShutDownFrontEnd()\r
9862 {\r
9863   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9864   DeleteClipboardTempFiles();\r
9865 }\r
9866 \r
9867 void\r
9868 BoardToTop()\r
9869 {\r
9870     if (IsIconic(hwndMain))\r
9871       ShowWindow(hwndMain, SW_RESTORE);\r
9872 \r
9873     SetActiveWindow(hwndMain);\r
9874 }\r
9875 \r
9876 /*\r
9877  * Prototypes for animation support routines\r
9878  */\r
9879 static void ScreenSquare(int column, int row, POINT * pt);\r
9880 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9881      POINT frames[], int * nFrames);\r
9882 \r
9883 \r
9884 #define kFactor 4\r
9885 \r
9886 void\r
9887 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9888 {       // [HGM] atomic: animate blast wave\r
9889         int i;\r
9890 \r
9891         explodeInfo.fromX = fromX;\r
9892         explodeInfo.fromY = fromY;\r
9893         explodeInfo.toX = toX;\r
9894         explodeInfo.toY = toY;\r
9895         for(i=1; i<4*kFactor; i++) {\r
9896             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9897             DrawPosition(FALSE, board);\r
9898             Sleep(appData.animSpeed);\r
9899         }\r
9900         explodeInfo.radius = 0;\r
9901         DrawPosition(TRUE, board);\r
9902 }\r
9903 \r
9904 void\r
9905 AnimateMove(board, fromX, fromY, toX, toY)\r
9906      Board board;\r
9907      int fromX;\r
9908      int fromY;\r
9909      int toX;\r
9910      int toY;\r
9911 {\r
9912   ChessSquare piece;\r
9913   int x = toX, y = toY;\r
9914   POINT start, finish, mid;\r
9915   POINT frames[kFactor * 2 + 1];\r
9916   int nFrames, n;\r
9917 \r
9918   if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();\r
9919 \r
9920   if (!appData.animate) return;\r
9921   if (doingSizing) return;\r
9922   if (fromY < 0 || fromX < 0) return;\r
9923   piece = board[fromY][fromX];\r
9924   if (piece >= EmptySquare) return;\r
9925 \r
9926   if(killX >= 0) toX = killX, toY = killY; // [HGM] lion: first to kill square\r
9927 \r
9928 again:\r
9929 \r
9930   ScreenSquare(fromX, fromY, &start);\r
9931   ScreenSquare(toX, toY, &finish);\r
9932 \r
9933   /* All moves except knight jumps move in straight line */\r
9934   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9935     mid.x = start.x + (finish.x - start.x) / 2;\r
9936     mid.y = start.y + (finish.y - start.y) / 2;\r
9937   } else {\r
9938     /* Knight: make straight movement then diagonal */\r
9939     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9940        mid.x = start.x + (finish.x - start.x) / 2;\r
9941        mid.y = start.y;\r
9942      } else {\r
9943        mid.x = start.x;\r
9944        mid.y = start.y + (finish.y - start.y) / 2;\r
9945      }\r
9946   }\r
9947   \r
9948   /* Don't use as many frames for very short moves */\r
9949   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9950     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9951   else\r
9952     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9953 \r
9954   animInfo.from.x = fromX;\r
9955   animInfo.from.y = fromY;\r
9956   animInfo.to.x = toX;\r
9957   animInfo.to.y = toY;\r
9958   animInfo.lastpos = start;\r
9959   animInfo.piece = piece;\r
9960   for (n = 0; n < nFrames; n++) {\r
9961     animInfo.pos = frames[n];\r
9962     DrawPosition(FALSE, NULL);\r
9963     animInfo.lastpos = animInfo.pos;\r
9964     Sleep(appData.animSpeed);\r
9965   }\r
9966   animInfo.pos = finish;\r
9967   DrawPosition(FALSE, NULL);\r
9968 \r
9969   if(toX != x || toY != y) { fromX = toX; fromY = toY; toX = x; toY = y; goto again; } // second leg\r
9970 \r
9971   animInfo.piece = EmptySquare;\r
9972   Explode(board, fromX, fromY, toX, toY);\r
9973 }\r
9974 \r
9975 /*      Convert board position to corner of screen rect and color       */\r
9976 \r
9977 static void\r
9978 ScreenSquare(column, row, pt)\r
9979      int column; int row; POINT * pt;\r
9980 {\r
9981   if (flipView) {\r
9982     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
9983     pt->y = lineGap + row * (squareSize + lineGap) + border;\r
9984   } else {\r
9985     pt->x = lineGap + column * (squareSize + lineGap) + border;\r
9986     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
9987   }\r
9988 }\r
9989 \r
9990 /*      Generate a series of frame coords from start->mid->finish.\r
9991         The movement rate doubles until the half way point is\r
9992         reached, then halves back down to the final destination,\r
9993         which gives a nice slow in/out effect. The algorithmn\r
9994         may seem to generate too many intermediates for short\r
9995         moves, but remember that the purpose is to attract the\r
9996         viewers attention to the piece about to be moved and\r
9997         then to where it ends up. Too few frames would be less\r
9998         noticeable.                                             */\r
9999 \r
10000 static void\r
10001 Tween(start, mid, finish, factor, frames, nFrames)\r
10002      POINT * start; POINT * mid;\r
10003      POINT * finish; int factor;\r
10004      POINT frames[]; int * nFrames;\r
10005 {\r
10006   int n, fraction = 1, count = 0;\r
10007 \r
10008   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10009   for (n = 0; n < factor; n++)\r
10010     fraction *= 2;\r
10011   for (n = 0; n < factor; n++) {\r
10012     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10013     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10014     count ++;\r
10015     fraction = fraction / 2;\r
10016   }\r
10017   \r
10018   /* Midpoint */\r
10019   frames[count] = *mid;\r
10020   count ++;\r
10021   \r
10022   /* Slow out, stepping 1/2, then 1/4, ... */\r
10023   fraction = 2;\r
10024   for (n = 0; n < factor; n++) {\r
10025     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10026     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10027     count ++;\r
10028     fraction = fraction * 2;\r
10029   }\r
10030   *nFrames = count;\r
10031 }\r
10032 \r
10033 void\r
10034 SettingsPopUp(ChessProgramState *cps)\r
10035 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
10036       EngineOptionsPopup(savedHwnd, cps);\r
10037 }\r
10038 \r
10039 int flock(int fid, int code)\r
10040 {\r
10041     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
10042     OVERLAPPED ov;\r
10043     ov.hEvent = NULL;\r
10044     ov.Offset = 0;\r
10045     ov.OffsetHigh = 0;\r
10046     switch(code) {\r
10047       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
10048       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
10049       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
10050       default: return -1;\r
10051     }\r
10052     return 0;\r
10053 }\r
10054 \r
10055 char *\r
10056 Col2Text (int n)\r
10057 {\r
10058     static int i=0;\r
10059     static char col[8][20];\r
10060     COLORREF color = *(COLORREF *) colorVariable[n];\r
10061     i = i+1 & 7;\r
10062     snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
10063     return col[i];\r
10064 }\r
10065 \r
10066 void\r
10067 ActivateTheme (int new)\r
10068 {   // Redo initialization of features depending on options that can occur in themes\r
10069    InitTextures();\r
10070    if(new) InitDrawingColors();\r
10071    fontBitmapSquareSize = 0; // request creation of new font pieces\r
10072    InitDrawingSizes(boardSize, 0);\r
10073    InvalidateRect(hwndMain, NULL, TRUE);\r
10074 }\r