Implement (inaccessible) dark squares
[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 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
113 void ChatPopUp P((char *s));\r
114 typedef struct {\r
115   ChessSquare piece;  \r
116   POINT pos;      /* window coordinates of current pos */\r
117   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
118   POINT from;     /* board coordinates of the piece's orig pos */\r
119   POINT to;       /* board coordinates of the piece's new pos */\r
120 } AnimInfo;\r
121 \r
122 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
123 \r
124 typedef struct {\r
125   POINT start;    /* window coordinates of start pos */\r
126   POINT pos;      /* window coordinates of current pos */\r
127   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
128   POINT from;     /* board coordinates of the piece's orig pos */\r
129   ChessSquare piece;\r
130 } DragInfo;\r
131 \r
132 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
133 \r
134 typedef struct {\r
135   POINT sq[2];    /* board coordinates of from, to squares */\r
136 } HighlightInfo;\r
137 \r
138 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
139 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
140 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
141 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
142 \r
143 typedef struct { // [HGM] atomic\r
144   int fromX, fromY, toX, toY, radius;\r
145 } ExplodeInfo;\r
146 \r
147 static ExplodeInfo explodeInfo;\r
148 \r
149 /* Window class names */\r
150 char szAppName[] = "WinBoard";\r
151 char szConsoleName[] = "WBConsole";\r
152 \r
153 /* Title bar text */\r
154 char szTitle[] = "WinBoard";\r
155 char szConsoleTitle[] = "I C S Interaction";\r
156 \r
157 char *programName;\r
158 char *settingsFileName;\r
159 Boolean saveSettingsOnExit;\r
160 char installDir[MSG_SIZ];\r
161 int errorExitStatus;\r
162 \r
163 BoardSize boardSize;\r
164 Boolean chessProgram;\r
165 //static int boardX, boardY;\r
166 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
167 int squareSize, lineGap, minorSize, border;\r
168 static int winW, winH;\r
169 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
170 static int logoHeight = 0;\r
171 static char messageText[MESSAGE_TEXT_MAX];\r
172 static int clockTimerEvent = 0;\r
173 static int loadGameTimerEvent = 0;\r
174 static int analysisTimerEvent = 0;\r
175 static DelayedEventCallback delayedTimerCallback;\r
176 static int delayedTimerEvent = 0;\r
177 static int buttonCount = 2;\r
178 char *icsTextMenuString;\r
179 char *icsNames;\r
180 char *firstChessProgramNames;\r
181 char *secondChessProgramNames;\r
182 \r
183 #define PALETTESIZE 256\r
184 \r
185 HINSTANCE hInst;          /* current instance */\r
186 Boolean alwaysOnTop = FALSE;\r
187 RECT boardRect;\r
188 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
189   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
190 COLORREF markerColor[8] = { 0x00FFFF, 0x0000FF, 0x00FF00, 0xFF0000, 0xFFFF00, 0xFF00FF, 0xFFFFFF, 0x000000 };\r
191 HPALETTE hPal;\r
192 ColorClass currentColorClass;\r
193 \r
194 static HWND savedHwnd;\r
195 HWND hCommPort = NULL;    /* currently open comm port */\r
196 static HWND hwndPause;    /* pause button */\r
197 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
198 static HBRUSH lightSquareBrush, darkSquareBrush,\r
199   blackSquareBrush, /* [HGM] for band between board and holdings */\r
200   explodeBrush,     /* [HGM] atomic */\r
201   markerBrush[8],   /* [HGM] markers */\r
202   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
203 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
204 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
205 static HPEN gridPen = NULL;\r
206 static HPEN highlightPen = NULL;\r
207 static HPEN premovePen = NULL;\r
208 static NPLOGPALETTE pLogPal;\r
209 static BOOL paletteChanged = FALSE;\r
210 static HICON iconWhite, iconBlack, iconCurrent;\r
211 static int doingSizing = FALSE;\r
212 static int lastSizing = 0;\r
213 static int prevStderrPort;\r
214 static HBITMAP userLogo;\r
215 \r
216 static HBITMAP liteBackTexture = NULL;\r
217 static HBITMAP darkBackTexture = NULL;\r
218 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
219 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
220 static int backTextureSquareSize = 0;\r
221 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
222 \r
223 #if __GNUC__ && !defined(_winmajor)\r
224 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
225 #else\r
226 \r
227 #if defined(_winmajor)\r
228 #define oldDialog (_winmajor < 4)\r
229 #else\r
230 #define oldDialog 0\r
231 #endif\r
232 #endif\r
233 \r
234 #define INTERNATIONAL\r
235 \r
236 #ifdef INTERNATIONAL\r
237 #  define _(s) T_(s)\r
238 #  define N_(s) s\r
239 #else\r
240 #  define _(s) s\r
241 #  define N_(s) s\r
242 #  define T_(s) s\r
243 #  define Translate(x, y)\r
244 #  define LoadLanguageFile(s)\r
245 #endif\r
246 \r
247 #ifdef INTERNATIONAL\r
248 \r
249 Boolean barbaric; // flag indicating if translation is needed\r
250 \r
251 // list of item numbers used in each dialog (used to alter language at run time)\r
252 \r
253 #define ABOUTBOX -1  /* not sure why these are needed */\r
254 #define ABOUTBOX2 -1\r
255 \r
256 int dialogItems[][42] = {\r
257 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
258 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
259   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
260 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,\r
261   OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL }, \r
262 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
263   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
264 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
265 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
266   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
267 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
268 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
269   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
270 { ABOUTBOX2, IDC_ChessBoard }, \r
271 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
272   OPT_GameListClose, IDC_GameListDoFilter }, \r
273 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
274 { DLG_Error, IDOK }, \r
275 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
276   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
277 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
278 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
279   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
280   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
281 { DLG_IndexNumber, IDC_Index }, \r
282 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
283 { DLG_TypeInName, IDOK, IDCANCEL }, \r
284 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
285   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
286 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
287   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
288   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
289   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
290   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
291   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
292   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
293 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
294   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
295   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
296   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
297   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
298   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
299   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
300   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
301   GPB_General, GPB_Alarm, OPT_AutoCreate }, \r
302 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
303   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
304   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
305   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
306   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
307   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
308   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
309   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
310 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
311   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
312   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
313   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
314   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
315   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
316   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
317   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
318   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
319 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
320   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
321   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
322   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
323   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
324 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
325 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
326   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
327 { DLG_MoveHistory }, \r
328 { DLG_EvalGraph }, \r
329 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
330 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
331 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
332   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
333   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
334   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
335 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
336   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
337   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
338 { 0 }\r
339 };\r
340 \r
341 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
342 static int lastChecked;\r
343 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
344 extern int tinyLayout;\r
345 extern char * menuBarText[][10];\r
346 \r
347 void\r
348 LoadLanguageFile(char *name)\r
349 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
350     FILE *f;\r
351     int i=0, j=0, n=0, k;\r
352     char buf[MSG_SIZ];\r
353 \r
354     if(!name || name[0] == NULLCHAR) return;\r
355       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
356     appData.language = oldLanguage;\r
357     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
358     if((f = fopen(buf, "r")) == NULL) return;\r
359     while((k = fgetc(f)) != EOF) {\r
360         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
361         languageBuf[i] = k;\r
362         if(k == '\n') {\r
363             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
364                 char *p;\r
365                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
366                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
367                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
368                         english[j] = languageBuf + n + 1; *p = 0;\r
369                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
370 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
371                     }\r
372                 }\r
373             }\r
374             n = i + 1;\r
375         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
376             switch(k) {\r
377               case 'n': k = '\n'; break;\r
378               case 'r': k = '\r'; break;\r
379               case 't': k = '\t'; break;\r
380             }\r
381             languageBuf[--i] = k;\r
382         }\r
383         i++;\r
384     }\r
385     fclose(f);\r
386     barbaric = (j != 0);\r
387     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
388 }\r
389 \r
390 char *\r
391 T_(char *s)\r
392 {   // return the translation of the given string\r
393     // efficiency can be improved a lot...\r
394     int i=0;\r
395     static char buf[MSG_SIZ];\r
396 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
397     if(!barbaric) return s;\r
398     if(!s) return ""; // sanity\r
399     while(english[i]) {\r
400         if(!strcmp(s, english[i])) return foreign[i];\r
401         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
402             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
403             return buf;\r
404         }\r
405         i++;\r
406     }\r
407     return s;\r
408 }\r
409 \r
410 void\r
411 Translate(HWND hDlg, int dialogID)\r
412 {   // translate all text items in the given dialog\r
413     int i=0, j, k;\r
414     char buf[MSG_SIZ], *s;\r
415     if(!barbaric) return;\r
416     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
417     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
418     GetWindowText( hDlg, buf, MSG_SIZ );\r
419     s = T_(buf);\r
420     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
421     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
422         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
423         if(strlen(buf) == 0) continue;\r
424         s = T_(buf);\r
425         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
426     }\r
427 }\r
428 \r
429 HMENU\r
430 TranslateOneMenu(int i, HMENU subMenu)\r
431 {\r
432     int j;\r
433     static MENUITEMINFO info;\r
434 \r
435     info.cbSize = sizeof(MENUITEMINFO);\r
436     info.fMask = MIIM_STATE | MIIM_TYPE;\r
437           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
438             char buf[MSG_SIZ];\r
439             info.dwTypeData = buf;\r
440             info.cch = sizeof(buf);\r
441             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
442             if(i < 10) {\r
443                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
444                 else menuText[i][j] = strdup(buf); // remember original on first change\r
445             }\r
446             if(buf[0] == NULLCHAR) continue;\r
447             info.dwTypeData = T_(buf);\r
448             info.cch = strlen(buf)+1;\r
449             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
450           }\r
451     return subMenu;\r
452 }\r
453 \r
454 void\r
455 TranslateMenus(int addLanguage)\r
456 {\r
457     int i;\r
458     WIN32_FIND_DATA fileData;\r
459     HANDLE hFind;\r
460 #define IDM_English 1970\r
461     if(1) {\r
462         HMENU mainMenu = GetMenu(hwndMain);\r
463         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
464           HMENU subMenu = GetSubMenu(mainMenu, i);\r
465           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
466                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
467           TranslateOneMenu(i, subMenu);\r
468         }\r
469         DrawMenuBar(hwndMain);\r
470     }\r
471 \r
472     if(!addLanguage) return;\r
473     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
474         HMENU mainMenu = GetMenu(hwndMain);\r
475         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
476         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
477         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
478         i = 0; lastChecked = IDM_English;\r
479         do {\r
480             char *p, *q = fileData.cFileName;\r
481             int checkFlag = MF_UNCHECKED;\r
482             languageFile[i] = strdup(q);\r
483             if(barbaric && !strcmp(oldLanguage, q)) {\r
484                 checkFlag = MF_CHECKED;\r
485                 lastChecked = IDM_English + i + 1;\r
486                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
487             }\r
488             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
489             p = strstr(fileData.cFileName, ".lng");\r
490             if(p) *p = 0;\r
491             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
492         } while(FindNextFile(hFind, &fileData));\r
493         FindClose(hFind);\r
494     }\r
495 }\r
496 \r
497 #endif\r
498 \r
499 #define IDM_RecentEngines 3000\r
500 \r
501 void\r
502 RecentEngineMenu (char *s)\r
503 {\r
504     if(appData.icsActive) return;\r
505     if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
506         HMENU mainMenu = GetMenu(hwndMain);\r
507         HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
508         int i=IDM_RecentEngines;\r
509         recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
510         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
511         while(*s) {\r
512           char *p = strchr(s, '\n');\r
513           if(p == NULL) return; // malformed!\r
514           *p = NULLCHAR;\r
515           AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
516           *p = '\n';\r
517           s = p+1;\r
518         }\r
519     }\r
520 }\r
521 \r
522 \r
523 typedef struct {\r
524   char *name;\r
525   int squareSize;\r
526   int lineGap;\r
527   int smallLayout;\r
528   int tinyLayout;\r
529   int cliWidth, cliHeight;\r
530 } SizeInfo;\r
531 \r
532 SizeInfo sizeInfo[] = \r
533 {\r
534   { "tiny",     21, 0, 1, 1, 0, 0 },\r
535   { "teeny",    25, 1, 1, 1, 0, 0 },\r
536   { "dinky",    29, 1, 1, 1, 0, 0 },\r
537   { "petite",   33, 1, 1, 1, 0, 0 },\r
538   { "slim",     37, 2, 1, 0, 0, 0 },\r
539   { "small",    40, 2, 1, 0, 0, 0 },\r
540   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
541   { "middling", 49, 2, 0, 0, 0, 0 },\r
542   { "average",  54, 2, 0, 0, 0, 0 },\r
543   { "moderate", 58, 3, 0, 0, 0, 0 },\r
544   { "medium",   64, 3, 0, 0, 0, 0 },\r
545   { "bulky",    72, 3, 0, 0, 0, 0 },\r
546   { "large",    80, 3, 0, 0, 0, 0 },\r
547   { "big",      87, 3, 0, 0, 0, 0 },\r
548   { "huge",     95, 3, 0, 0, 0, 0 },\r
549   { "giant",    108, 3, 0, 0, 0, 0 },\r
550   { "colossal", 116, 4, 0, 0, 0, 0 },\r
551   { "titanic",  129, 4, 0, 0, 0, 0 },\r
552   { NULL, 0, 0, 0, 0, 0, 0 }\r
553 };\r
554 \r
555 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
556 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
557 {\r
558   { 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
559   { 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
560   { 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
561   { 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
562   { 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
563   { 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
564   { 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
565   { 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
566   { 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
567   { 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
568   { 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
569   { 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
570   { 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
571   { 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
572   { 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
573   { 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
574   { 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
575   { 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
576 };\r
577 \r
578 MyFont *font[NUM_SIZES][NUM_FONTS];\r
579 \r
580 typedef struct {\r
581   char *label;\r
582   int id;\r
583   HWND hwnd;\r
584   WNDPROC wndproc;\r
585 } MyButtonDesc;\r
586 \r
587 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
588 #define N_BUTTONS 5\r
589 \r
590 MyButtonDesc buttonDesc[N_BUTTONS] =\r
591 {\r
592   {"<<", IDM_ToStart, NULL, NULL},\r
593   {"<", IDM_Backward, NULL, NULL},\r
594   {"P", IDM_Pause, NULL, NULL},\r
595   {">", IDM_Forward, NULL, NULL},\r
596   {">>", IDM_ToEnd, NULL, NULL},\r
597 };\r
598 \r
599 int tinyLayout = 0, smallLayout = 0;\r
600 #define MENU_BAR_ITEMS 9\r
601 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
602   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
603   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
604 };\r
605 \r
606 \r
607 MySound sounds[(int)NSoundClasses];\r
608 MyTextAttribs textAttribs[(int)NColorClasses];\r
609 \r
610 MyColorizeAttribs colorizeAttribs[] = {\r
611   { (COLORREF)0, 0, N_("Shout Text") },\r
612   { (COLORREF)0, 0, N_("SShout/CShout") },\r
613   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
614   { (COLORREF)0, 0, N_("Channel Text") },\r
615   { (COLORREF)0, 0, N_("Kibitz Text") },\r
616   { (COLORREF)0, 0, N_("Tell Text") },\r
617   { (COLORREF)0, 0, N_("Challenge Text") },\r
618   { (COLORREF)0, 0, N_("Request Text") },\r
619   { (COLORREF)0, 0, N_("Seek Text") },\r
620   { (COLORREF)0, 0, N_("Normal Text") },\r
621   { (COLORREF)0, 0, N_("None") }\r
622 };\r
623 \r
624 \r
625 \r
626 static char *commentTitle;\r
627 static char *commentText;\r
628 static int commentIndex;\r
629 static Boolean editComment = FALSE;\r
630 \r
631 \r
632 char errorTitle[MSG_SIZ];\r
633 char errorMessage[2*MSG_SIZ];\r
634 HWND errorDialog = NULL;\r
635 BOOLEAN moveErrorMessageUp = FALSE;\r
636 BOOLEAN consoleEcho = TRUE;\r
637 CHARFORMAT consoleCF;\r
638 COLORREF consoleBackgroundColor;\r
639 \r
640 char *programVersion;\r
641 \r
642 #define CPReal 1\r
643 #define CPComm 2\r
644 #define CPSock 3\r
645 #define CPRcmd 4\r
646 typedef int CPKind;\r
647 \r
648 typedef struct {\r
649   CPKind kind;\r
650   HANDLE hProcess;\r
651   DWORD pid;\r
652   HANDLE hTo;\r
653   HANDLE hFrom;\r
654   SOCKET sock;\r
655   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
656 } ChildProc;\r
657 \r
658 #define INPUT_SOURCE_BUF_SIZE 4096\r
659 \r
660 typedef struct _InputSource {\r
661   CPKind kind;\r
662   HANDLE hFile;\r
663   SOCKET sock;\r
664   int lineByLine;\r
665   HANDLE hThread;\r
666   DWORD id;\r
667   char buf[INPUT_SOURCE_BUF_SIZE];\r
668   char *next;\r
669   DWORD count;\r
670   int error;\r
671   InputCallback func;\r
672   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
673   VOIDSTAR closure;\r
674 } InputSource;\r
675 \r
676 InputSource *consoleInputSource;\r
677 \r
678 DCB dcb;\r
679 \r
680 /* forward */\r
681 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
682 VOID ConsoleCreate();\r
683 LRESULT CALLBACK\r
684   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
685 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
686 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
687 VOID ParseCommSettings(char *arg, DCB *dcb);\r
688 LRESULT CALLBACK\r
689   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
690 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
691 void ParseIcsTextMenu(char *icsTextMenuString);\r
692 VOID PopUpNameDialog(char firstchar);\r
693 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
694 \r
695 /* [AS] */\r
696 int NewGameFRC();\r
697 int GameListOptions();\r
698 \r
699 int dummy; // [HGM] for obsolete args\r
700 \r
701 HWND hwndMain = NULL;        /* root window*/\r
702 HWND hwndConsole = NULL;\r
703 HWND commentDialog = NULL;\r
704 HWND moveHistoryDialog = NULL;\r
705 HWND evalGraphDialog = NULL;\r
706 HWND engineOutputDialog = NULL;\r
707 HWND gameListDialog = NULL;\r
708 HWND editTagsDialog = NULL;\r
709 \r
710 int commentUp = FALSE;\r
711 \r
712 WindowPlacement wpMain;\r
713 WindowPlacement wpConsole;\r
714 WindowPlacement wpComment;\r
715 WindowPlacement wpMoveHistory;\r
716 WindowPlacement wpEvalGraph;\r
717 WindowPlacement wpEngineOutput;\r
718 WindowPlacement wpGameList;\r
719 WindowPlacement wpTags;\r
720 \r
721 VOID EngineOptionsPopup(); // [HGM] settings\r
722 \r
723 VOID GothicPopUp(char *title, VariantClass variant);\r
724 /*\r
725  * Setting "frozen" should disable all user input other than deleting\r
726  * the window.  We do this while engines are initializing themselves.\r
727  */\r
728 static int frozen = 0;\r
729 static int oldMenuItemState[MENU_BAR_ITEMS];\r
730 void FreezeUI()\r
731 {\r
732   HMENU hmenu;\r
733   int i;\r
734 \r
735   if (frozen) return;\r
736   frozen = 1;\r
737   hmenu = GetMenu(hwndMain);\r
738   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
739     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
740   }\r
741   DrawMenuBar(hwndMain);\r
742 }\r
743 \r
744 /* Undo a FreezeUI */\r
745 void ThawUI()\r
746 {\r
747   HMENU hmenu;\r
748   int i;\r
749 \r
750   if (!frozen) return;\r
751   frozen = 0;\r
752   hmenu = GetMenu(hwndMain);\r
753   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
754     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
755   }\r
756   DrawMenuBar(hwndMain);\r
757 }\r
758 \r
759 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
760 \r
761 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
762 #ifdef JAWS\r
763 #include "jaws.c"\r
764 #else\r
765 #define JAWS_INIT\r
766 #define JAWS_ARGS\r
767 #define JAWS_ALT_INTERCEPT\r
768 #define JAWS_KBUP_NAVIGATION\r
769 #define JAWS_KBDOWN_NAVIGATION\r
770 #define JAWS_MENU_ITEMS\r
771 #define JAWS_SILENCE\r
772 #define JAWS_REPLAY\r
773 #define JAWS_ACCEL\r
774 #define JAWS_COPYRIGHT\r
775 #define JAWS_DELETE(X) X\r
776 #define SAYMACHINEMOVE()\r
777 #define SAY(X)\r
778 #endif\r
779 \r
780 /*---------------------------------------------------------------------------*\\r
781  *\r
782  * WinMain\r
783  *\r
784 \*---------------------------------------------------------------------------*/\r
785 \r
786 int APIENTRY\r
787 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
788         LPSTR lpCmdLine, int nCmdShow)\r
789 {\r
790   MSG msg;\r
791   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
792 //  INITCOMMONCONTROLSEX ex;\r
793 \r
794   debugFP = stderr;\r
795 \r
796   LoadLibrary("RICHED32.DLL");\r
797   consoleCF.cbSize = sizeof(CHARFORMAT);\r
798 \r
799   if (!InitApplication(hInstance)) {\r
800     return (FALSE);\r
801   }\r
802   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
803     return (FALSE);\r
804   }\r
805 \r
806   JAWS_INIT\r
807   TranslateMenus(1);\r
808 \r
809 //  InitCommonControlsEx(&ex);\r
810   InitCommonControls();\r
811 \r
812   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
813   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
814   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
815 \r
816   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
817 \r
818   while (GetMessage(&msg, /* message structure */\r
819                     NULL, /* handle of window receiving the message */\r
820                     0,    /* lowest message to examine */\r
821                     0))   /* highest message to examine */\r
822     {\r
823 \r
824       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
825         // [HGM] navigate: switch between all windows with tab\r
826         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
827         int i, currentElement = 0;\r
828 \r
829         // first determine what element of the chain we come from (if any)\r
830         if(appData.icsActive) {\r
831             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
832             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
833         }\r
834         if(engineOutputDialog && EngineOutputIsUp()) {\r
835             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
836             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
837         }\r
838         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
839             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
840         }\r
841         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
842         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
843         if(msg.hwnd == e1)                 currentElement = 2; else\r
844         if(msg.hwnd == e2)                 currentElement = 3; else\r
845         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
846         if(msg.hwnd == mh)                currentElement = 4; else\r
847         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
848         if(msg.hwnd == hText)  currentElement = 5; else\r
849         if(msg.hwnd == hInput) currentElement = 6; else\r
850         for (i = 0; i < N_BUTTONS; i++) {\r
851             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
852         }\r
853 \r
854         // determine where to go to\r
855         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
856           do {\r
857             currentElement = (currentElement + direction) % 7;\r
858             switch(currentElement) {\r
859                 case 0:\r
860                   h = hwndMain; break; // passing this case always makes the loop exit\r
861                 case 1:\r
862                   h = buttonDesc[0].hwnd; break; // could be NULL\r
863                 case 2:\r
864                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
865                   h = e1; break;\r
866                 case 3:\r
867                   if(!EngineOutputIsUp()) continue;\r
868                   h = e2; break;\r
869                 case 4:\r
870                   if(!MoveHistoryIsUp()) continue;\r
871                   h = mh; break;\r
872 //              case 6: // input to eval graph does not seem to get here!\r
873 //                if(!EvalGraphIsUp()) continue;\r
874 //                h = evalGraphDialog; break;\r
875                 case 5:\r
876                   if(!appData.icsActive) continue;\r
877                   SAY("display");\r
878                   h = hText; break;\r
879                 case 6:\r
880                   if(!appData.icsActive) continue;\r
881                   SAY("input");\r
882                   h = hInput; break;\r
883             }\r
884           } while(h == 0);\r
885 \r
886           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
887           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
888           SetFocus(h);\r
889 \r
890           continue; // this message now has been processed\r
891         }\r
892       }\r
893 \r
894       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
895           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
896           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
897           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
898           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
899           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
900           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
901           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
902           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
903           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
904         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
905         for(i=0; i<MAX_CHAT; i++) \r
906             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
907                 done = 1; break;\r
908         }\r
909         if(done) continue; // [HGM] chat: end patch\r
910         TranslateMessage(&msg); /* Translates virtual key codes */\r
911         DispatchMessage(&msg);  /* Dispatches message to window */\r
912       }\r
913     }\r
914 \r
915 \r
916   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
917 }\r
918 \r
919 /*---------------------------------------------------------------------------*\\r
920  *\r
921  * Initialization functions\r
922  *\r
923 \*---------------------------------------------------------------------------*/\r
924 \r
925 void\r
926 SetUserLogo()\r
927 {   // update user logo if necessary\r
928     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
929 \r
930     if(appData.autoLogo) {\r
931           curName = UserName();\r
932           if(strcmp(curName, oldUserName)) {\r
933                 GetCurrentDirectory(MSG_SIZ, dir);\r
934                 SetCurrentDirectory(installDir);\r
935                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
936                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
937                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
938                 if(userLogo == NULL)\r
939                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
940                 SetCurrentDirectory(dir); /* return to prev directory */\r
941           }\r
942     }\r
943 }\r
944 \r
945 BOOL\r
946 InitApplication(HINSTANCE hInstance)\r
947 {\r
948   WNDCLASS wc;\r
949 \r
950   /* Fill in window class structure with parameters that describe the */\r
951   /* main window. */\r
952 \r
953   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
954   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
955   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
956   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
957   wc.hInstance     = hInstance;         /* Owner of this class */\r
958   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
959   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
960   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
961   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
962   wc.lpszClassName = szAppName;                 /* Name to register as */\r
963 \r
964   /* Register the window class and return success/failure code. */\r
965   if (!RegisterClass(&wc)) return FALSE;\r
966 \r
967   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
968   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
969   wc.cbClsExtra    = 0;\r
970   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
971   wc.hInstance     = hInstance;\r
972   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
973   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
974   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
975   wc.lpszMenuName  = NULL;\r
976   wc.lpszClassName = szConsoleName;\r
977 \r
978   if (!RegisterClass(&wc)) return FALSE;\r
979   return TRUE;\r
980 }\r
981 \r
982 \r
983 /* Set by InitInstance, used by EnsureOnScreen */\r
984 int screenHeight, screenWidth;\r
985 \r
986 void\r
987 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
988 {\r
989 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
990   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
991   if (*x > screenWidth - 32) *x = 0;\r
992   if (*y > screenHeight - 32) *y = 0;\r
993   if (*x < minX) *x = minX;\r
994   if (*y < minY) *y = minY;\r
995 }\r
996 \r
997 VOID\r
998 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
999 {\r
1000   char buf[MSG_SIZ], dir[MSG_SIZ];\r
1001   GetCurrentDirectory(MSG_SIZ, dir);\r
1002   SetCurrentDirectory(installDir);\r
1003   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
1004       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1005 \r
1006       if (cps->programLogo == NULL && appData.debugMode) {\r
1007           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1008       }\r
1009   } else if(appData.autoLogo) {\r
1010       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1011         char *opponent = "";\r
1012         if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;\r
1013         if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;\r
1014         sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);\r
1015         if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {\r
1016             sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1017             cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1018         }\r
1019       } else\r
1020       if(appData.directory[n] && appData.directory[n][0]) {\r
1021         SetCurrentDirectory(appData.directory[n]);\r
1022         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1023       }\r
1024   }\r
1025   SetCurrentDirectory(dir); /* return to prev directory */\r
1026 }\r
1027 \r
1028 VOID\r
1029 InitTextures()\r
1030 {\r
1031   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1032   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1033   \r
1034   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1035       if(liteBackTexture) DeleteObject(liteBackTexture);\r
1036       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1037       liteBackTextureMode = appData.liteBackTextureMode;\r
1038 \r
1039       if (liteBackTexture == NULL && appData.debugMode) {\r
1040           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1041       }\r
1042   }\r
1043   \r
1044   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1045       if(darkBackTexture) DeleteObject(darkBackTexture);\r
1046       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1047       darkBackTextureMode = appData.darkBackTextureMode;\r
1048 \r
1049       if (darkBackTexture == NULL && appData.debugMode) {\r
1050           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1051       }\r
1052   }\r
1053 }\r
1054 \r
1055 BOOL\r
1056 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1057 {\r
1058   HWND hwnd; /* Main window handle. */\r
1059   int ibs;\r
1060   WINDOWPLACEMENT wp;\r
1061   char *filepart;\r
1062 \r
1063   hInst = hInstance;    /* Store instance handle in our global variable */\r
1064   programName = szAppName;\r
1065 \r
1066   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1067     *filepart = NULLCHAR;\r
1068     SetCurrentDirectory(installDir);\r
1069   } else {\r
1070     GetCurrentDirectory(MSG_SIZ, installDir);\r
1071   }\r
1072   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1073   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
1074   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1075   /* xboard, and older WinBoards, controlled the move sound with the\r
1076      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1077      always turn the option on (so that the backend will call us),\r
1078      then let the user turn the sound off by setting it to silence if\r
1079      desired.  To accommodate old winboard.ini files saved by old\r
1080      versions of WinBoard, we also turn off the sound if the option\r
1081      was initially set to false. [HGM] taken out of InitAppData */\r
1082   if (!appData.ringBellAfterMoves) {\r
1083     sounds[(int)SoundMove].name = strdup("");\r
1084     appData.ringBellAfterMoves = TRUE;\r
1085   }\r
1086   if (appData.debugMode) {\r
1087     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1088     setbuf(debugFP, NULL);\r
1089   }\r
1090 \r
1091   LoadLanguageFile(appData.language);\r
1092 \r
1093   InitBackEnd1();\r
1094 \r
1095 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1096 //  InitEngineUCI( installDir, &second );\r
1097 \r
1098   /* Create a main window for this application instance. */\r
1099   hwnd = CreateWindow(szAppName, szTitle,\r
1100                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1101                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1102                       NULL, NULL, hInstance, NULL);\r
1103   hwndMain = hwnd;\r
1104 \r
1105   /* If window could not be created, return "failure" */\r
1106   if (!hwnd) {\r
1107     return (FALSE);\r
1108   }\r
1109 \r
1110   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1111   LoadLogo(&first, 0, FALSE);\r
1112   LoadLogo(&second, 1, appData.icsActive);\r
1113 \r
1114   SetUserLogo();\r
1115 \r
1116   iconWhite = LoadIcon(hInstance, "icon_white");\r
1117   iconBlack = LoadIcon(hInstance, "icon_black");\r
1118   iconCurrent = iconWhite;\r
1119   InitDrawingColors();\r
1120   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1121   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1122   InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args\r
1123   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1124     /* Compute window size for each board size, and use the largest\r
1125        size that fits on this screen as the default. */\r
1126     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1127     if (boardSize == (BoardSize)-1 &&\r
1128         winH <= screenHeight\r
1129            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1130         && winW <= screenWidth) {\r
1131       boardSize = (BoardSize)ibs;\r
1132     }\r
1133   }\r
1134 \r
1135   InitDrawingSizes(boardSize, 0);\r
1136   RecentEngineMenu(appData.recentEngineList);\r
1137   InitMenuChecks();\r
1138   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1139 \r
1140   /* [AS] Load textures if specified */\r
1141   InitTextures();\r
1142 \r
1143   mysrandom( (unsigned) time(NULL) );\r
1144 \r
1145   /* [AS] Restore layout */\r
1146   if( wpMoveHistory.visible ) {\r
1147       MoveHistoryPopUp();\r
1148   }\r
1149 \r
1150   if( wpEvalGraph.visible ) {\r
1151       EvalGraphPopUp();\r
1152   }\r
1153 \r
1154   if( wpEngineOutput.visible ) {\r
1155       EngineOutputPopUp();\r
1156   }\r
1157 \r
1158   /* Make the window visible; update its client area; and return "success" */\r
1159   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1160   wp.length = sizeof(WINDOWPLACEMENT);\r
1161   wp.flags = 0;\r
1162   wp.showCmd = nCmdShow;\r
1163   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1164   wp.rcNormalPosition.left = wpMain.x;\r
1165   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1166   wp.rcNormalPosition.top = wpMain.y;\r
1167   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1168   SetWindowPlacement(hwndMain, &wp);\r
1169 \r
1170   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1171 \r
1172   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1173                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1174 \r
1175   if (hwndConsole) {\r
1176 #if AOT_CONSOLE\r
1177     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1178                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1179 #endif\r
1180     ShowWindow(hwndConsole, nCmdShow);\r
1181     SetActiveWindow(hwndConsole);\r
1182   }\r
1183   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1184   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1185 \r
1186   return TRUE;\r
1187 \r
1188 }\r
1189 \r
1190 VOID\r
1191 InitMenuChecks()\r
1192 {\r
1193   HMENU hmenu = GetMenu(hwndMain);\r
1194 \r
1195   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1196                         MF_BYCOMMAND|((appData.icsActive &&\r
1197                                        *appData.icsCommPort != NULLCHAR) ?\r
1198                                       MF_ENABLED : MF_GRAYED));\r
1199   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1200                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1201                                      MF_CHECKED : MF_UNCHECKED));\r
1202 }\r
1203 \r
1204 //---------------------------------------------------------------------------------------------------------\r
1205 \r
1206 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1207 #define XBOARD FALSE\r
1208 \r
1209 #define OPTCHAR "/"\r
1210 #define SEPCHAR "="\r
1211 #define TOPLEVEL 0\r
1212 \r
1213 #include "args.h"\r
1214 \r
1215 // front-end part of option handling\r
1216 \r
1217 VOID\r
1218 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1219 {\r
1220   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1221   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1222   DeleteDC(hdc);\r
1223   lf->lfWidth = 0;\r
1224   lf->lfEscapement = 0;\r
1225   lf->lfOrientation = 0;\r
1226   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1227   lf->lfItalic = mfp->italic;\r
1228   lf->lfUnderline = mfp->underline;\r
1229   lf->lfStrikeOut = mfp->strikeout;\r
1230   lf->lfCharSet = mfp->charset;\r
1231   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1232   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1233   lf->lfQuality = DEFAULT_QUALITY;\r
1234   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1235     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1236 }\r
1237 \r
1238 void\r
1239 CreateFontInMF(MyFont *mf)\r
1240\r
1241   LFfromMFP(&mf->lf, &mf->mfp);\r
1242   if (mf->hf) DeleteObject(mf->hf);\r
1243   mf->hf = CreateFontIndirect(&mf->lf);\r
1244 }\r
1245 \r
1246 // [HGM] This platform-dependent table provides the location for storing the color info\r
1247 void *\r
1248 colorVariable[] = {\r
1249   &whitePieceColor, \r
1250   &blackPieceColor, \r
1251   &lightSquareColor,\r
1252   &darkSquareColor, \r
1253   &highlightSquareColor,\r
1254   &premoveHighlightColor,\r
1255   NULL,\r
1256   &consoleBackgroundColor,\r
1257   &appData.fontForeColorWhite,\r
1258   &appData.fontBackColorWhite,\r
1259   &appData.fontForeColorBlack,\r
1260   &appData.fontBackColorBlack,\r
1261   &appData.evalHistColorWhite,\r
1262   &appData.evalHistColorBlack,\r
1263   &appData.highlightArrowColor,\r
1264 };\r
1265 \r
1266 /* Command line font name parser.  NULL name means do nothing.\r
1267    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1268    For backward compatibility, syntax without the colon is also\r
1269    accepted, but font names with digits in them won't work in that case.\r
1270 */\r
1271 VOID\r
1272 ParseFontName(char *name, MyFontParams *mfp)\r
1273 {\r
1274   char *p, *q;\r
1275   if (name == NULL) return;\r
1276   p = name;\r
1277   q = strchr(p, ':');\r
1278   if (q) {\r
1279     if (q - p >= sizeof(mfp->faceName))\r
1280       ExitArgError(_("Font name too long:"), name, TRUE);\r
1281     memcpy(mfp->faceName, p, q - p);\r
1282     mfp->faceName[q - p] = NULLCHAR;\r
1283     p = q + 1;\r
1284   } else {\r
1285     q = mfp->faceName;\r
1286 \r
1287     while (*p && !isdigit(*p)) {\r
1288       *q++ = *p++;\r
1289       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1290         ExitArgError(_("Font name too long:"), name, TRUE);\r
1291     }\r
1292     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1293     *q = NULLCHAR;\r
1294   }\r
1295   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1296   mfp->pointSize = (float) atof(p);\r
1297   mfp->bold = (strchr(p, 'b') != NULL);\r
1298   mfp->italic = (strchr(p, 'i') != NULL);\r
1299   mfp->underline = (strchr(p, 'u') != NULL);\r
1300   mfp->strikeout = (strchr(p, 's') != NULL);\r
1301   mfp->charset = DEFAULT_CHARSET;\r
1302   q = strchr(p, 'c');\r
1303   if (q)\r
1304     mfp->charset = (BYTE) atoi(q+1);\r
1305 }\r
1306 \r
1307 void\r
1308 ParseFont(char *name, int number)\r
1309 { // wrapper to shield back-end from 'font'\r
1310   ParseFontName(name, &font[boardSize][number]->mfp);\r
1311 }\r
1312 \r
1313 void\r
1314 SetFontDefaults()\r
1315 { // in WB  we have a 2D array of fonts; this initializes their description\r
1316   int i, j;\r
1317   /* Point font array elements to structures and\r
1318      parse default font names */\r
1319   for (i=0; i<NUM_FONTS; i++) {\r
1320     for (j=0; j<NUM_SIZES; j++) {\r
1321       font[j][i] = &fontRec[j][i];\r
1322       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1323     }\r
1324   }\r
1325 }\r
1326 \r
1327 void\r
1328 CreateFonts()\r
1329 { // here we create the actual fonts from the selected descriptions\r
1330   int i, j;\r
1331   for (i=0; i<NUM_FONTS; i++) {\r
1332     for (j=0; j<NUM_SIZES; j++) {\r
1333       CreateFontInMF(font[j][i]);\r
1334     }\r
1335   }\r
1336 }\r
1337 /* Color name parser.\r
1338    X version accepts X color names, but this one\r
1339    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1340 COLORREF\r
1341 ParseColorName(char *name)\r
1342 {\r
1343   int red, green, blue, count;\r
1344   char buf[MSG_SIZ];\r
1345 \r
1346   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1347   if (count != 3) {\r
1348     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1349       &red, &green, &blue);\r
1350   }\r
1351   if (count != 3) {\r
1352     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1353     DisplayError(buf, 0);\r
1354     return RGB(0, 0, 0);\r
1355   }\r
1356   return PALETTERGB(red, green, blue);\r
1357 }\r
1358 \r
1359 void\r
1360 ParseColor(int n, char *name)\r
1361 { // for WinBoard the color is an int, which needs to be derived from the string\r
1362   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1363 }\r
1364 \r
1365 void\r
1366 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1367 {\r
1368   char *e = argValue;\r
1369   int eff = 0;\r
1370 \r
1371   while (*e) {\r
1372     if (*e == 'b')      eff |= CFE_BOLD;\r
1373     else if (*e == 'i') eff |= CFE_ITALIC;\r
1374     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1375     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1376     else if (*e == '#' || isdigit(*e)) break;\r
1377     e++;\r
1378   }\r
1379   *effects = eff;\r
1380   *color   = ParseColorName(e);\r
1381 }\r
1382 \r
1383 void\r
1384 ParseTextAttribs(ColorClass cc, char *s)\r
1385 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1386     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1387     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1388 }\r
1389 \r
1390 void\r
1391 ParseBoardSize(void *addr, char *name)\r
1392 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1393   BoardSize bs = SizeTiny;\r
1394   while (sizeInfo[bs].name != NULL) {\r
1395     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1396         *(BoardSize *)addr = bs;\r
1397         return;\r
1398     }\r
1399     bs++;\r
1400   }\r
1401   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1402 }\r
1403 \r
1404 void\r
1405 LoadAllSounds()\r
1406 { // [HGM] import name from appData first\r
1407   ColorClass cc;\r
1408   SoundClass sc;\r
1409   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1410     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1411     textAttribs[cc].sound.data = NULL;\r
1412     MyLoadSound(&textAttribs[cc].sound);\r
1413   }\r
1414   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1415     textAttribs[cc].sound.name = strdup("");\r
1416     textAttribs[cc].sound.data = NULL;\r
1417   }\r
1418   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1419     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1420     sounds[sc].data = NULL;\r
1421     MyLoadSound(&sounds[sc]);\r
1422   }\r
1423 }\r
1424 \r
1425 void\r
1426 SetCommPortDefaults()\r
1427 {\r
1428    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1429   dcb.DCBlength = sizeof(DCB);\r
1430   dcb.BaudRate = 9600;\r
1431   dcb.fBinary = TRUE;\r
1432   dcb.fParity = FALSE;\r
1433   dcb.fOutxCtsFlow = FALSE;\r
1434   dcb.fOutxDsrFlow = FALSE;\r
1435   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1436   dcb.fDsrSensitivity = FALSE;\r
1437   dcb.fTXContinueOnXoff = TRUE;\r
1438   dcb.fOutX = FALSE;\r
1439   dcb.fInX = FALSE;\r
1440   dcb.fNull = FALSE;\r
1441   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1442   dcb.fAbortOnError = FALSE;\r
1443   dcb.ByteSize = 7;\r
1444   dcb.Parity = SPACEPARITY;\r
1445   dcb.StopBits = ONESTOPBIT;\r
1446 }\r
1447 \r
1448 // [HGM] args: these three cases taken out to stay in front-end\r
1449 void\r
1450 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1451 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1452         // while the curent board size determines the element. This system should be ported to XBoard.\r
1453         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1454         int bs;\r
1455         for (bs=0; bs<NUM_SIZES; bs++) {\r
1456           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1457           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1458           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1459             ad->argName, mfp->faceName, mfp->pointSize,\r
1460             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1461             mfp->bold ? "b" : "",\r
1462             mfp->italic ? "i" : "",\r
1463             mfp->underline ? "u" : "",\r
1464             mfp->strikeout ? "s" : "",\r
1465             (int)mfp->charset);\r
1466         }\r
1467       }\r
1468 \r
1469 void\r
1470 ExportSounds()\r
1471 { // [HGM] copy the names from the internal WB variables to appData\r
1472   ColorClass cc;\r
1473   SoundClass sc;\r
1474   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1475     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1476   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1477     (&appData.soundMove)[sc] = sounds[sc].name;\r
1478 }\r
1479 \r
1480 void\r
1481 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1482 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1483         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1484         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1485           (ta->effects & CFE_BOLD) ? "b" : "",\r
1486           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1487           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1488           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1489           (ta->effects) ? " " : "",\r
1490           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1491       }\r
1492 \r
1493 void\r
1494 SaveColor(FILE *f, ArgDescriptor *ad)\r
1495 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1496         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1497         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1498           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1499 }\r
1500 \r
1501 void\r
1502 SaveBoardSize(FILE *f, char *name, void *addr)\r
1503 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1504   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1505 }\r
1506 \r
1507 void\r
1508 ParseCommPortSettings(char *s)\r
1509 { // wrapper to keep dcb from back-end\r
1510   ParseCommSettings(s, &dcb);\r
1511 }\r
1512 \r
1513 void\r
1514 GetWindowCoords()\r
1515 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1516   GetActualPlacement(hwndMain, &wpMain);\r
1517   GetActualPlacement(hwndConsole, &wpConsole);\r
1518   GetActualPlacement(commentDialog, &wpComment);\r
1519   GetActualPlacement(editTagsDialog, &wpTags);\r
1520   GetActualPlacement(gameListDialog, &wpGameList);\r
1521   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1522   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1523   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1524 }\r
1525 \r
1526 void\r
1527 PrintCommPortSettings(FILE *f, char *name)\r
1528 { // wrapper to shield back-end from DCB\r
1529       PrintCommSettings(f, name, &dcb);\r
1530 }\r
1531 \r
1532 int\r
1533 MySearchPath(char *installDir, char *name, char *fullname)\r
1534 {\r
1535   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1536   if(name[0]== '%') {\r
1537     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1538     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1539       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1540       *strchr(buf, '%') = 0;\r
1541       strcat(fullname, getenv(buf));\r
1542       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1543     }\r
1544     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1545     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1546     return (int) strlen(fullname);\r
1547   }\r
1548   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1549 }\r
1550 \r
1551 int\r
1552 MyGetFullPathName(char *name, char *fullname)\r
1553 {\r
1554   char *dummy;\r
1555   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1556 }\r
1557 \r
1558 int\r
1559 MainWindowUp()\r
1560 { // [HGM] args: allows testing if main window is realized from back-end\r
1561   return hwndMain != NULL;\r
1562 }\r
1563 \r
1564 void\r
1565 PopUpStartupDialog()\r
1566 {\r
1567     FARPROC lpProc;\r
1568     \r
1569     LoadLanguageFile(appData.language);\r
1570     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1571     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1572     FreeProcInstance(lpProc);\r
1573 }\r
1574 \r
1575 /*---------------------------------------------------------------------------*\\r
1576  *\r
1577  * GDI board drawing routines\r
1578  *\r
1579 \*---------------------------------------------------------------------------*/\r
1580 \r
1581 /* [AS] Draw square using background texture */\r
1582 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1583 {\r
1584     XFORM   x;\r
1585 \r
1586     if( mode == 0 ) {\r
1587         return; /* Should never happen! */\r
1588     }\r
1589 \r
1590     SetGraphicsMode( dst, GM_ADVANCED );\r
1591 \r
1592     switch( mode ) {\r
1593     case 1:\r
1594         /* Identity */\r
1595         break;\r
1596     case 2:\r
1597         /* X reflection */\r
1598         x.eM11 = -1.0;\r
1599         x.eM12 = 0;\r
1600         x.eM21 = 0;\r
1601         x.eM22 = 1.0;\r
1602         x.eDx = (FLOAT) dw + dx - 1;\r
1603         x.eDy = 0;\r
1604         dx = 0;\r
1605         SetWorldTransform( dst, &x );\r
1606         break;\r
1607     case 3:\r
1608         /* Y reflection */\r
1609         x.eM11 = 1.0;\r
1610         x.eM12 = 0;\r
1611         x.eM21 = 0;\r
1612         x.eM22 = -1.0;\r
1613         x.eDx = 0;\r
1614         x.eDy = (FLOAT) dh + dy - 1;\r
1615         dy = 0;\r
1616         SetWorldTransform( dst, &x );\r
1617         break;\r
1618     case 4:\r
1619         /* X/Y flip */\r
1620         x.eM11 = 0;\r
1621         x.eM12 = 1.0;\r
1622         x.eM21 = 1.0;\r
1623         x.eM22 = 0;\r
1624         x.eDx = (FLOAT) dx;\r
1625         x.eDy = (FLOAT) dy;\r
1626         dx = 0;\r
1627         dy = 0;\r
1628         SetWorldTransform( dst, &x );\r
1629         break;\r
1630     }\r
1631 \r
1632     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1633 \r
1634     x.eM11 = 1.0;\r
1635     x.eM12 = 0;\r
1636     x.eM21 = 0;\r
1637     x.eM22 = 1.0;\r
1638     x.eDx = 0;\r
1639     x.eDy = 0;\r
1640     SetWorldTransform( dst, &x );\r
1641 \r
1642     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1643 }\r
1644 \r
1645 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1646 enum {\r
1647     PM_WP = (int) WhitePawn, \r
1648     PM_WN = (int) WhiteKnight, \r
1649     PM_WB = (int) WhiteBishop, \r
1650     PM_WR = (int) WhiteRook, \r
1651     PM_WQ = (int) WhiteQueen, \r
1652     PM_WF = (int) WhiteFerz, \r
1653     PM_WW = (int) WhiteWazir, \r
1654     PM_WE = (int) WhiteAlfil, \r
1655     PM_WM = (int) WhiteMan, \r
1656     PM_WO = (int) WhiteCannon, \r
1657     PM_WU = (int) WhiteUnicorn, \r
1658     PM_WH = (int) WhiteNightrider, \r
1659     PM_WA = (int) WhiteAngel, \r
1660     PM_WC = (int) WhiteMarshall, \r
1661     PM_WAB = (int) WhiteCardinal, \r
1662     PM_WD = (int) WhiteDragon, \r
1663     PM_WL = (int) WhiteLance, \r
1664     PM_WS = (int) WhiteCobra, \r
1665     PM_WV = (int) WhiteFalcon, \r
1666     PM_WSG = (int) WhiteSilver, \r
1667     PM_WG = (int) WhiteGrasshopper, \r
1668     PM_WK = (int) WhiteKing,\r
1669     PM_BP = (int) BlackPawn, \r
1670     PM_BN = (int) BlackKnight, \r
1671     PM_BB = (int) BlackBishop, \r
1672     PM_BR = (int) BlackRook, \r
1673     PM_BQ = (int) BlackQueen, \r
1674     PM_BF = (int) BlackFerz, \r
1675     PM_BW = (int) BlackWazir, \r
1676     PM_BE = (int) BlackAlfil, \r
1677     PM_BM = (int) BlackMan,\r
1678     PM_BO = (int) BlackCannon, \r
1679     PM_BU = (int) BlackUnicorn, \r
1680     PM_BH = (int) BlackNightrider, \r
1681     PM_BA = (int) BlackAngel, \r
1682     PM_BC = (int) BlackMarshall, \r
1683     PM_BG = (int) BlackGrasshopper, \r
1684     PM_BAB = (int) BlackCardinal,\r
1685     PM_BD = (int) BlackDragon,\r
1686     PM_BL = (int) BlackLance,\r
1687     PM_BS = (int) BlackCobra,\r
1688     PM_BV = (int) BlackFalcon,\r
1689     PM_BSG = (int) BlackSilver,\r
1690     PM_BK = (int) BlackKing\r
1691 };\r
1692 \r
1693 static HFONT hPieceFont = NULL;\r
1694 static HBITMAP hPieceMask[(int) EmptySquare];\r
1695 static HBITMAP hPieceFace[(int) EmptySquare];\r
1696 static int fontBitmapSquareSize = 0;\r
1697 static char pieceToFontChar[(int) EmptySquare] =\r
1698                               { 'p', 'n', 'b', 'r', 'q', \r
1699                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1700                       'k', 'o', 'm', 'v', 't', 'w', \r
1701                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1702                                                               'l' };\r
1703 \r
1704 extern BOOL SetCharTable( char *table, const char * map );\r
1705 /* [HGM] moved to backend.c */\r
1706 \r
1707 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1708 {\r
1709     HBRUSH hbrush;\r
1710     BYTE r1 = GetRValue( color );\r
1711     BYTE g1 = GetGValue( color );\r
1712     BYTE b1 = GetBValue( color );\r
1713     BYTE r2 = r1 / 2;\r
1714     BYTE g2 = g1 / 2;\r
1715     BYTE b2 = b1 / 2;\r
1716     RECT rc;\r
1717 \r
1718     /* Create a uniform background first */\r
1719     hbrush = CreateSolidBrush( color );\r
1720     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1721     FillRect( hdc, &rc, hbrush );\r
1722     DeleteObject( hbrush );\r
1723     \r
1724     if( mode == 1 ) {\r
1725         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1726         int steps = squareSize / 2;\r
1727         int i;\r
1728 \r
1729         for( i=0; i<steps; i++ ) {\r
1730             BYTE r = r1 - (r1-r2) * i / steps;\r
1731             BYTE g = g1 - (g1-g2) * i / steps;\r
1732             BYTE b = b1 - (b1-b2) * i / steps;\r
1733 \r
1734             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1735             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1736             FillRect( hdc, &rc, hbrush );\r
1737             DeleteObject(hbrush);\r
1738         }\r
1739     }\r
1740     else if( mode == 2 ) {\r
1741         /* Diagonal gradient, good more or less for every piece */\r
1742         POINT triangle[3];\r
1743         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1744         HBRUSH hbrush_old;\r
1745         int steps = squareSize;\r
1746         int i;\r
1747 \r
1748         triangle[0].x = squareSize - steps;\r
1749         triangle[0].y = squareSize;\r
1750         triangle[1].x = squareSize;\r
1751         triangle[1].y = squareSize;\r
1752         triangle[2].x = squareSize;\r
1753         triangle[2].y = squareSize - steps;\r
1754 \r
1755         for( i=0; i<steps; i++ ) {\r
1756             BYTE r = r1 - (r1-r2) * i / steps;\r
1757             BYTE g = g1 - (g1-g2) * i / steps;\r
1758             BYTE b = b1 - (b1-b2) * i / steps;\r
1759 \r
1760             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1761             hbrush_old = SelectObject( hdc, hbrush );\r
1762             Polygon( hdc, triangle, 3 );\r
1763             SelectObject( hdc, hbrush_old );\r
1764             DeleteObject(hbrush);\r
1765             triangle[0].x++;\r
1766             triangle[2].y++;\r
1767         }\r
1768 \r
1769         SelectObject( hdc, hpen );\r
1770     }\r
1771 }\r
1772 \r
1773 /*\r
1774     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1775     seems to work ok. The main problem here is to find the "inside" of a chess\r
1776     piece: follow the steps as explained below.\r
1777 */\r
1778 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1779 {\r
1780     HBITMAP hbm;\r
1781     HBITMAP hbm_old;\r
1782     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1783     RECT rc;\r
1784     SIZE sz;\r
1785     POINT pt;\r
1786     int backColor = whitePieceColor; \r
1787     int foreColor = blackPieceColor;\r
1788     \r
1789     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1790         backColor = appData.fontBackColorWhite;\r
1791         foreColor = appData.fontForeColorWhite;\r
1792     }\r
1793     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1794         backColor = appData.fontBackColorBlack;\r
1795         foreColor = appData.fontForeColorBlack;\r
1796     }\r
1797 \r
1798     /* Mask */\r
1799     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1800 \r
1801     hbm_old = SelectObject( hdc, hbm );\r
1802 \r
1803     rc.left = 0;\r
1804     rc.top = 0;\r
1805     rc.right = squareSize;\r
1806     rc.bottom = squareSize;\r
1807 \r
1808     /* Step 1: background is now black */\r
1809     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1810 \r
1811     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1812 \r
1813     pt.x = (squareSize - sz.cx) / 2;\r
1814     pt.y = (squareSize - sz.cy) / 2;\r
1815 \r
1816     SetBkMode( hdc, TRANSPARENT );\r
1817     SetTextColor( hdc, chroma );\r
1818     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1819     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1820 \r
1821     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1822     /* Step 3: the area outside the piece is filled with white */\r
1823 //    FloodFill( hdc, 0, 0, chroma );\r
1824     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1825     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1826     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1827     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1828     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1829     /* \r
1830         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1831         but if the start point is not inside the piece we're lost!\r
1832         There should be a better way to do this... if we could create a region or path\r
1833         from the fill operation we would be fine for example.\r
1834     */\r
1835 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1836     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1837 \r
1838     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1839         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1840         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1841 \r
1842         SelectObject( dc2, bm2 );\r
1843         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1844         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1845         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1846         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1847         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1848 \r
1849         DeleteDC( dc2 );\r
1850         DeleteObject( bm2 );\r
1851     }\r
1852 \r
1853     SetTextColor( hdc, 0 );\r
1854     /* \r
1855         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1856         draw the piece again in black for safety.\r
1857     */\r
1858     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1859 \r
1860     SelectObject( hdc, hbm_old );\r
1861 \r
1862     if( hPieceMask[index] != NULL ) {\r
1863         DeleteObject( hPieceMask[index] );\r
1864     }\r
1865 \r
1866     hPieceMask[index] = hbm;\r
1867 \r
1868     /* Face */\r
1869     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1870 \r
1871     SelectObject( hdc, hbm );\r
1872 \r
1873     {\r
1874         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1875         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1876         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1877 \r
1878         SelectObject( dc1, hPieceMask[index] );\r
1879         SelectObject( dc2, bm2 );\r
1880         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1881         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1882         \r
1883         /* \r
1884             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1885             the piece background and deletes (makes transparent) the rest.\r
1886             Thanks to that mask, we are free to paint the background with the greates\r
1887             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1888             We use this, to make gradients and give the pieces a "roundish" look.\r
1889         */\r
1890         SetPieceBackground( hdc, backColor, 2 );\r
1891         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1892 \r
1893         DeleteDC( dc2 );\r
1894         DeleteDC( dc1 );\r
1895         DeleteObject( bm2 );\r
1896     }\r
1897 \r
1898     SetTextColor( hdc, foreColor );\r
1899     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1900 \r
1901     SelectObject( hdc, hbm_old );\r
1902 \r
1903     if( hPieceFace[index] != NULL ) {\r
1904         DeleteObject( hPieceFace[index] );\r
1905     }\r
1906 \r
1907     hPieceFace[index] = hbm;\r
1908 }\r
1909 \r
1910 static int TranslatePieceToFontPiece( int piece )\r
1911 {\r
1912     switch( piece ) {\r
1913     case BlackPawn:\r
1914         return PM_BP;\r
1915     case BlackKnight:\r
1916         return PM_BN;\r
1917     case BlackBishop:\r
1918         return PM_BB;\r
1919     case BlackRook:\r
1920         return PM_BR;\r
1921     case BlackQueen:\r
1922         return PM_BQ;\r
1923     case BlackKing:\r
1924         return PM_BK;\r
1925     case WhitePawn:\r
1926         return PM_WP;\r
1927     case WhiteKnight:\r
1928         return PM_WN;\r
1929     case WhiteBishop:\r
1930         return PM_WB;\r
1931     case WhiteRook:\r
1932         return PM_WR;\r
1933     case WhiteQueen:\r
1934         return PM_WQ;\r
1935     case WhiteKing:\r
1936         return PM_WK;\r
1937 \r
1938     case BlackAngel:\r
1939         return PM_BA;\r
1940     case BlackMarshall:\r
1941         return PM_BC;\r
1942     case BlackFerz:\r
1943         return PM_BF;\r
1944     case BlackNightrider:\r
1945         return PM_BH;\r
1946     case BlackAlfil:\r
1947         return PM_BE;\r
1948     case BlackWazir:\r
1949         return PM_BW;\r
1950     case BlackUnicorn:\r
1951         return PM_BU;\r
1952     case BlackCannon:\r
1953         return PM_BO;\r
1954     case BlackGrasshopper:\r
1955         return PM_BG;\r
1956     case BlackMan:\r
1957         return PM_BM;\r
1958     case BlackSilver:\r
1959         return PM_BSG;\r
1960     case BlackLance:\r
1961         return PM_BL;\r
1962     case BlackFalcon:\r
1963         return PM_BV;\r
1964     case BlackCobra:\r
1965         return PM_BS;\r
1966     case BlackCardinal:\r
1967         return PM_BAB;\r
1968     case BlackDragon:\r
1969         return PM_BD;\r
1970 \r
1971     case WhiteAngel:\r
1972         return PM_WA;\r
1973     case WhiteMarshall:\r
1974         return PM_WC;\r
1975     case WhiteFerz:\r
1976         return PM_WF;\r
1977     case WhiteNightrider:\r
1978         return PM_WH;\r
1979     case WhiteAlfil:\r
1980         return PM_WE;\r
1981     case WhiteWazir:\r
1982         return PM_WW;\r
1983     case WhiteUnicorn:\r
1984         return PM_WU;\r
1985     case WhiteCannon:\r
1986         return PM_WO;\r
1987     case WhiteGrasshopper:\r
1988         return PM_WG;\r
1989     case WhiteMan:\r
1990         return PM_WM;\r
1991     case WhiteSilver:\r
1992         return PM_WSG;\r
1993     case WhiteLance:\r
1994         return PM_WL;\r
1995     case WhiteFalcon:\r
1996         return PM_WV;\r
1997     case WhiteCobra:\r
1998         return PM_WS;\r
1999     case WhiteCardinal:\r
2000         return PM_WAB;\r
2001     case WhiteDragon:\r
2002         return PM_WD;\r
2003     }\r
2004 \r
2005     return 0;\r
2006 }\r
2007 \r
2008 void CreatePiecesFromFont()\r
2009 {\r
2010     LOGFONT lf;\r
2011     HDC hdc_window = NULL;\r
2012     HDC hdc = NULL;\r
2013     HFONT hfont_old;\r
2014     int fontHeight;\r
2015     int i;\r
2016 \r
2017     if( fontBitmapSquareSize < 0 ) {\r
2018         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2019         return;\r
2020     }\r
2021 \r
2022     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2023             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2024         fontBitmapSquareSize = -1;\r
2025         return;\r
2026     }\r
2027 \r
2028     if( fontBitmapSquareSize != squareSize ) {\r
2029         hdc_window = GetDC( hwndMain );\r
2030         hdc = CreateCompatibleDC( hdc_window );\r
2031 \r
2032         if( hPieceFont != NULL ) {\r
2033             DeleteObject( hPieceFont );\r
2034         }\r
2035         else {\r
2036             for( i=0; i<=(int)BlackKing; i++ ) {\r
2037                 hPieceMask[i] = NULL;\r
2038                 hPieceFace[i] = NULL;\r
2039             }\r
2040         }\r
2041 \r
2042         fontHeight = 75;\r
2043 \r
2044         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2045             fontHeight = appData.fontPieceSize;\r
2046         }\r
2047 \r
2048         fontHeight = (fontHeight * squareSize) / 100;\r
2049 \r
2050         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2051         lf.lfWidth = 0;\r
2052         lf.lfEscapement = 0;\r
2053         lf.lfOrientation = 0;\r
2054         lf.lfWeight = FW_NORMAL;\r
2055         lf.lfItalic = 0;\r
2056         lf.lfUnderline = 0;\r
2057         lf.lfStrikeOut = 0;\r
2058         lf.lfCharSet = DEFAULT_CHARSET;\r
2059         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2060         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2061         lf.lfQuality = PROOF_QUALITY;\r
2062         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2063         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2064         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2065 \r
2066         hPieceFont = CreateFontIndirect( &lf );\r
2067 \r
2068         if( hPieceFont == NULL ) {\r
2069             fontBitmapSquareSize = -2;\r
2070         }\r
2071         else {\r
2072             /* Setup font-to-piece character table */\r
2073             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2074                 /* No (or wrong) global settings, try to detect the font */\r
2075                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2076                     /* Alpha */\r
2077                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2078                 }\r
2079                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2080                     /* DiagramTT* family */\r
2081                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2082                 }\r
2083                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2084                     /* Fairy symbols */\r
2085                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2086                 }\r
2087                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2088                     /* Good Companion (Some characters get warped as literal :-( */\r
2089                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2090                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2091                     SetCharTable(pieceToFontChar, s);\r
2092                 }\r
2093                 else {\r
2094                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2095                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2096                 }\r
2097             }\r
2098 \r
2099             /* Create bitmaps */\r
2100             hfont_old = SelectObject( hdc, hPieceFont );\r
2101             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2102                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2103                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2104 \r
2105             SelectObject( hdc, hfont_old );\r
2106 \r
2107             fontBitmapSquareSize = squareSize;\r
2108         }\r
2109     }\r
2110 \r
2111     if( hdc != NULL ) {\r
2112         DeleteDC( hdc );\r
2113     }\r
2114 \r
2115     if( hdc_window != NULL ) {\r
2116         ReleaseDC( hwndMain, hdc_window );\r
2117     }\r
2118 }\r
2119 \r
2120 HBITMAP\r
2121 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2122 {\r
2123   char name[128], buf[MSG_SIZ];\r
2124 \r
2125     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2126   if(appData.pieceDirectory[0]) {\r
2127     HBITMAP res;\r
2128     snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);\r
2129     res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
2130     if(res) return res;\r
2131   }\r
2132   if (gameInfo.event &&\r
2133       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2134       strcmp(name, "k80s") == 0) {\r
2135     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2136   }\r
2137   return LoadBitmap(hinst, name);\r
2138 }\r
2139 \r
2140 \r
2141 /* Insert a color into the program's logical palette\r
2142    structure.  This code assumes the given color is\r
2143    the result of the RGB or PALETTERGB macro, and it\r
2144    knows how those macros work (which is documented).\r
2145 */\r
2146 VOID\r
2147 InsertInPalette(COLORREF color)\r
2148 {\r
2149   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2150 \r
2151   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2152     DisplayFatalError(_("Too many colors"), 0, 1);\r
2153     pLogPal->palNumEntries--;\r
2154     return;\r
2155   }\r
2156 \r
2157   pe->peFlags = (char) 0;\r
2158   pe->peRed = (char) (0xFF & color);\r
2159   pe->peGreen = (char) (0xFF & (color >> 8));\r
2160   pe->peBlue = (char) (0xFF & (color >> 16));\r
2161   return;\r
2162 }\r
2163 \r
2164 \r
2165 VOID\r
2166 InitDrawingColors()\r
2167 {\r
2168   int i;\r
2169   if (pLogPal == NULL) {\r
2170     /* Allocate enough memory for a logical palette with\r
2171      * PALETTESIZE entries and set the size and version fields\r
2172      * of the logical palette structure.\r
2173      */\r
2174     pLogPal = (NPLOGPALETTE)\r
2175       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2176                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2177     pLogPal->palVersion    = 0x300;\r
2178   }\r
2179   pLogPal->palNumEntries = 0;\r
2180 \r
2181   InsertInPalette(lightSquareColor);\r
2182   InsertInPalette(darkSquareColor);\r
2183   InsertInPalette(whitePieceColor);\r
2184   InsertInPalette(blackPieceColor);\r
2185   InsertInPalette(highlightSquareColor);\r
2186   InsertInPalette(premoveHighlightColor);\r
2187 \r
2188   /*  create a logical color palette according the information\r
2189    *  in the LOGPALETTE structure.\r
2190    */\r
2191   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2192 \r
2193   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2194   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2195   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2196   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2197   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2198   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2199   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2200     for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers\r
2201 \r
2202    /* [AS] Force rendering of the font-based pieces */\r
2203   if( fontBitmapSquareSize > 0 ) {\r
2204     fontBitmapSquareSize = 0;\r
2205   }\r
2206 }\r
2207 \r
2208 \r
2209 int\r
2210 BoardWidth(int boardSize, int n)\r
2211 { /* [HGM] argument n added to allow different width and height */\r
2212   int lineGap = sizeInfo[boardSize].lineGap;\r
2213 \r
2214   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2215       lineGap = appData.overrideLineGap;\r
2216   }\r
2217 \r
2218   return (n + 1) * lineGap +\r
2219           n * sizeInfo[boardSize].squareSize;\r
2220 }\r
2221 \r
2222 /* Respond to board resize by dragging edge */\r
2223 VOID\r
2224 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2225 {\r
2226   BoardSize newSize = NUM_SIZES - 1;\r
2227   static int recurse = 0;\r
2228   if (IsIconic(hwndMain)) return;\r
2229   if (recurse > 0) return;\r
2230   recurse++;\r
2231   while (newSize > 0) {\r
2232         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2233         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2234            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2235     newSize--;\r
2236   } \r
2237   boardSize = newSize;\r
2238   InitDrawingSizes(boardSize, flags);\r
2239   recurse--;\r
2240 }\r
2241 \r
2242 \r
2243 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2244 \r
2245 VOID\r
2246 InitDrawingSizes(BoardSize boardSize, int flags)\r
2247 {\r
2248   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2249   ChessSquare piece;\r
2250   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2251   HDC hdc;\r
2252   SIZE clockSize, messageSize;\r
2253   HFONT oldFont;\r
2254   char buf[MSG_SIZ];\r
2255   char *str;\r
2256   HMENU hmenu = GetMenu(hwndMain);\r
2257   RECT crect, wrect, oldRect;\r
2258   int offby;\r
2259   LOGBRUSH logbrush;\r
2260   VariantClass v = gameInfo.variant;\r
2261 \r
2262   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2263   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2264 \r
2265   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2266   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2267   if(boardSize == -1) return;     // no size defined yet; abort (to allow early call of InitPosition)\r
2268   oldBoardSize = boardSize;\r
2269 \r
2270   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2271   { // correct board size to one where built-in pieces exist\r
2272     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2273        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2274       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2275       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2276       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy ) {\r
2277       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2278       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2279                                    boardSize = SizeMiddling;\r
2280     }\r
2281   }\r
2282   if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2283 \r
2284   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2285   oldRect.top = wpMain.y;\r
2286   oldRect.right = wpMain.x + wpMain.width;\r
2287   oldRect.bottom = wpMain.y + wpMain.height;\r
2288 \r
2289   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2290   smallLayout = sizeInfo[boardSize].smallLayout;\r
2291   squareSize = sizeInfo[boardSize].squareSize;\r
2292   lineGap = sizeInfo[boardSize].lineGap;\r
2293   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2294   border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;\r
2295 \r
2296   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2297       lineGap = appData.overrideLineGap;\r
2298   }\r
2299 \r
2300   if (tinyLayout != oldTinyLayout) {\r
2301     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2302     if (tinyLayout) {\r
2303       style &= ~WS_SYSMENU;\r
2304       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2305                  "&Minimize\tCtrl+F4");\r
2306     } else {\r
2307       style |= WS_SYSMENU;\r
2308       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2309     }\r
2310     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2311 \r
2312     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2313       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2314         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2315     }\r
2316     DrawMenuBar(hwndMain);\r
2317   }\r
2318 \r
2319   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;\r
2320   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;\r
2321 \r
2322   /* Get text area sizes */\r
2323   hdc = GetDC(hwndMain);\r
2324   if (appData.clockMode) {\r
2325     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2326   } else {\r
2327     snprintf(buf, MSG_SIZ, _("White"));\r
2328   }\r
2329   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2330   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2331   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2332   str = _("We only care about the height here");\r
2333   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2334   SelectObject(hdc, oldFont);\r
2335   ReleaseDC(hwndMain, hdc);\r
2336 \r
2337   /* Compute where everything goes */\r
2338   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2339         /* [HGM] logo: if either logo is on, reserve space for it */\r
2340         logoHeight =  2*clockSize.cy;\r
2341         leftLogoRect.left   = OUTER_MARGIN;\r
2342         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2343         leftLogoRect.top    = OUTER_MARGIN;\r
2344         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2345 \r
2346         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2347         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2348         rightLogoRect.top    = OUTER_MARGIN;\r
2349         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2350 \r
2351 \r
2352     whiteRect.left = leftLogoRect.right;\r
2353     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2354     whiteRect.top = OUTER_MARGIN;\r
2355     whiteRect.bottom = whiteRect.top + logoHeight;\r
2356 \r
2357     blackRect.right = rightLogoRect.left;\r
2358     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2359     blackRect.top = whiteRect.top;\r
2360     blackRect.bottom = whiteRect.bottom;\r
2361   } else {\r
2362     whiteRect.left = OUTER_MARGIN;\r
2363     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2364     whiteRect.top = OUTER_MARGIN;\r
2365     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2366 \r
2367     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2368     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2369     blackRect.top = whiteRect.top;\r
2370     blackRect.bottom = whiteRect.bottom;\r
2371 \r
2372     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2373   }\r
2374 \r
2375   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2376   if (appData.showButtonBar) {\r
2377     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2378       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2379   } else {\r
2380     messageRect.right = OUTER_MARGIN + boardWidth;\r
2381   }\r
2382   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2383   messageRect.bottom = messageRect.top + messageSize.cy;\r
2384 \r
2385   boardRect.left = OUTER_MARGIN;\r
2386   boardRect.right = boardRect.left + boardWidth;\r
2387   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2388   boardRect.bottom = boardRect.top + boardHeight;\r
2389 \r
2390   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2391   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2392   oldTinyLayout = tinyLayout;\r
2393   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2394   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2395     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2396   winW *= 1 + twoBoards;\r
2397   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2398   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2399   wpMain.height = winH; //       without disturbing window attachments\r
2400   GetWindowRect(hwndMain, &wrect);\r
2401   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2402                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2403 \r
2404   // [HGM] placement: let attached windows follow size change.\r
2405   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2406   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2407   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2408   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2409   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2410 \r
2411   /* compensate if menu bar wrapped */\r
2412   GetClientRect(hwndMain, &crect);\r
2413   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2414   wpMain.height += offby;\r
2415   switch (flags) {\r
2416   case WMSZ_TOPLEFT:\r
2417     SetWindowPos(hwndMain, NULL, \r
2418                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2419                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2420     break;\r
2421 \r
2422   case WMSZ_TOPRIGHT:\r
2423   case WMSZ_TOP:\r
2424     SetWindowPos(hwndMain, NULL, \r
2425                  wrect.left, wrect.bottom - wpMain.height, \r
2426                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2427     break;\r
2428 \r
2429   case WMSZ_BOTTOMLEFT:\r
2430   case WMSZ_LEFT:\r
2431     SetWindowPos(hwndMain, NULL, \r
2432                  wrect.right - wpMain.width, wrect.top, \r
2433                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2434     break;\r
2435 \r
2436   case WMSZ_BOTTOMRIGHT:\r
2437   case WMSZ_BOTTOM:\r
2438   case WMSZ_RIGHT:\r
2439   default:\r
2440     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2441                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2442     break;\r
2443   }\r
2444 \r
2445   hwndPause = NULL;\r
2446   for (i = 0; i < N_BUTTONS; i++) {\r
2447     if (buttonDesc[i].hwnd != NULL) {\r
2448       DestroyWindow(buttonDesc[i].hwnd);\r
2449       buttonDesc[i].hwnd = NULL;\r
2450     }\r
2451     if (appData.showButtonBar) {\r
2452       buttonDesc[i].hwnd =\r
2453         CreateWindow("BUTTON", buttonDesc[i].label,\r
2454                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2455                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2456                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2457                      (HMENU) buttonDesc[i].id,\r
2458                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2459       if (tinyLayout) {\r
2460         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2461                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2462                     MAKELPARAM(FALSE, 0));\r
2463       }\r
2464       if (buttonDesc[i].id == IDM_Pause)\r
2465         hwndPause = buttonDesc[i].hwnd;\r
2466       buttonDesc[i].wndproc = (WNDPROC)\r
2467         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2468     }\r
2469   }\r
2470   if (gridPen != NULL) DeleteObject(gridPen);\r
2471   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2472   if (premovePen != NULL) DeleteObject(premovePen);\r
2473   if (lineGap != 0) {\r
2474     logbrush.lbStyle = BS_SOLID;\r
2475     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2476     gridPen =\r
2477       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2478                    lineGap, &logbrush, 0, NULL);\r
2479     logbrush.lbColor = highlightSquareColor;\r
2480     highlightPen =\r
2481       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2482                    lineGap, &logbrush, 0, NULL);\r
2483 \r
2484     logbrush.lbColor = premoveHighlightColor; \r
2485     premovePen =\r
2486       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2487                    lineGap, &logbrush, 0, NULL);\r
2488 \r
2489     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2490     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2491       gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;\r
2492       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2493         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2494       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2495         BOARD_WIDTH * (squareSize + lineGap) + border;\r
2496       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2497     }\r
2498     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2499       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;\r
2500       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2501         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2502         lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2503       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2504         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;\r
2505       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2506     }\r
2507   }\r
2508 \r
2509   /* [HGM] Licensing requirement */\r
2510 #ifdef GOTHIC\r
2511   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2512 #endif\r
2513 #ifdef FALCON\r
2514   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2515 #endif\r
2516   GothicPopUp( "", VariantNormal);\r
2517 \r
2518 \r
2519 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2520 \r
2521   /* Load piece bitmaps for this board size */\r
2522   for (i=0; i<=2; i++) {\r
2523     for (piece = WhitePawn;\r
2524          (int) piece < (int) BlackPawn;\r
2525          piece = (ChessSquare) ((int) piece + 1)) {\r
2526       if (pieceBitmap[i][piece] != NULL)\r
2527         DeleteObject(pieceBitmap[i][piece]);\r
2528     }\r
2529   }\r
2530 \r
2531   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2532   // Orthodox Chess pieces\r
2533   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2534   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2535   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2536   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2537   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2538   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2539   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2540   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2541   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2542   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2543   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2544   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2545   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2546   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2547   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2548   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2549     // in Shogi, Hijack the unused Queen for Lance\r
2550     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2551     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2552     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2553   } else {\r
2554     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2555     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2556     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2557   }\r
2558 \r
2559   if(squareSize <= 72 && squareSize >= 33) { \r
2560     /* A & C are available in most sizes now */\r
2561     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2562       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2563       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2564       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2565       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2566       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2567       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2568       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2569       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2570       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2571       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2572       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2573       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2574     } else { // Smirf-like\r
2575       if(gameInfo.variant == VariantSChess) {\r
2576         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2577         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2578         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2579       } else {\r
2580         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2581         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2582         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2583       }\r
2584     }\r
2585     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2586       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2587       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2588       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2589     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2590       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2591       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2592       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2593     } else { // WinBoard standard\r
2594       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2595       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2596       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2597     }\r
2598   }\r
2599 \r
2600 \r
2601   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2602     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2603     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2604     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2605     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2606     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2607     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2608     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2609     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2610     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2611     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2612     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2613     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2614     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2615     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2616     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2617     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2618     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2619     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2620     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2621     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2622     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2623     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2624     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2625     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2626     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2627     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2628     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2629     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2630     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2631     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2632 \r
2633     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2634       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2635       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2636       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2637       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2638       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2639       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2640       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2641       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2642       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2643       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2644       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2645       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2646     } else {\r
2647       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2648       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2649       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2650       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2651       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2652       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2653       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2654       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2655       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2656       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2657       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2658       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2659     }\r
2660 \r
2661   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2662     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2663     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2664     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2665     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2666     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2667     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2668     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2669     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2670     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2671     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2672     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2673     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2674     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2675     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2676   }\r
2677 \r
2678 \r
2679   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2680   /* special Shogi support in this size */\r
2681   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2682       for (piece = WhitePawn;\r
2683            (int) piece < (int) BlackPawn;\r
2684            piece = (ChessSquare) ((int) piece + 1)) {\r
2685         if (pieceBitmap[i][piece] != NULL)\r
2686           DeleteObject(pieceBitmap[i][piece]);\r
2687       }\r
2688     }\r
2689   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2690   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2691   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2692   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2693   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2694   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2695   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2696   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2697   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2698   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2699   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2700   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2701   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2702   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2703   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2704   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2705   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2706   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2707   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2708   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2709   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2710   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2711   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2712   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2713   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2714   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2715   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2716   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2717   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2718   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2719   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2720   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2721   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2722   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2723   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2724   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2725   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2726   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2727   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2728   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2729   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2730   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2731   minorSize = 0;\r
2732   }\r
2733 }\r
2734 \r
2735 HBITMAP\r
2736 PieceBitmap(ChessSquare p, int kind)\r
2737 {\r
2738   if ((int) p >= (int) BlackPawn)\r
2739     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2740 \r
2741   return pieceBitmap[kind][(int) p];\r
2742 }\r
2743 \r
2744 /***************************************************************/\r
2745 \r
2746 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2747 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2748 /*\r
2749 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2750 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2751 */\r
2752 \r
2753 VOID\r
2754 SquareToPos(int row, int column, int * x, int * y)\r
2755 {\r
2756   if (flipView) {\r
2757     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
2758     *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;\r
2759   } else {\r
2760     *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;\r
2761     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
2762   }\r
2763 }\r
2764 \r
2765 VOID\r
2766 DrawCoordsOnDC(HDC hdc)\r
2767 {\r
2768   static char files[] = "0123456789012345678901221098765432109876543210";\r
2769   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2770   char str[2] = { NULLCHAR, NULLCHAR };\r
2771   int oldMode, oldAlign, x, y, start, i;\r
2772   HFONT oldFont;\r
2773   HBRUSH oldBrush;\r
2774 \r
2775   if (!appData.showCoords)\r
2776     return;\r
2777 \r
2778   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2779 \r
2780   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2781   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2782   oldAlign = GetTextAlign(hdc);\r
2783   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2784 \r
2785   y = boardRect.top + lineGap;\r
2786   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2787 \r
2788   if(border) {\r
2789     SetTextAlign(hdc, TA_RIGHT|TA_TOP);\r
2790     x += border - lineGap - 4; y += squareSize - 6;\r
2791   } else\r
2792   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2793   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2794     str[0] = files[start + i];\r
2795     ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);\r
2796     y += squareSize + lineGap;\r
2797   }\r
2798 \r
2799   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2800 \r
2801   if(border) {\r
2802     SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2803     x += -border + 4; y += border - squareSize + 6;\r
2804   } else\r
2805   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2806   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2807     str[0] = ranks[start + i];\r
2808     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2809     x += squareSize + lineGap;\r
2810   }    \r
2811 \r
2812   SelectObject(hdc, oldBrush);\r
2813   SetBkMode(hdc, oldMode);\r
2814   SetTextAlign(hdc, oldAlign);\r
2815   SelectObject(hdc, oldFont);\r
2816 }\r
2817 \r
2818 VOID\r
2819 DrawGridOnDC(HDC hdc)\r
2820 {\r
2821   HPEN oldPen;\r
2822  \r
2823   if (lineGap != 0) {\r
2824     oldPen = SelectObject(hdc, gridPen);\r
2825     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2826     SelectObject(hdc, oldPen);\r
2827   }\r
2828 }\r
2829 \r
2830 #define HIGHLIGHT_PEN 0\r
2831 #define PREMOVE_PEN   1\r
2832 \r
2833 VOID\r
2834 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2835 {\r
2836   int x1, y1;\r
2837   HPEN oldPen, hPen;\r
2838   if (lineGap == 0) return;\r
2839   if (flipView) {\r
2840     x1 = boardRect.left +\r
2841       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;\r
2842     y1 = boardRect.top +\r
2843       lineGap/2 + y * (squareSize + lineGap) + border;\r
2844   } else {\r
2845     x1 = boardRect.left +\r
2846       lineGap/2 + x * (squareSize + lineGap) + border;\r
2847     y1 = boardRect.top +\r
2848       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;\r
2849   }\r
2850   hPen = pen ? premovePen : highlightPen;\r
2851   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2852   MoveToEx(hdc, x1, y1, NULL);\r
2853   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2854   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2855   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2856   LineTo(hdc, x1, y1);\r
2857   SelectObject(hdc, oldPen);\r
2858 }\r
2859 \r
2860 VOID\r
2861 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2862 {\r
2863   int i;\r
2864   for (i=0; i<2; i++) {\r
2865     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2866       DrawHighlightOnDC(hdc, TRUE,\r
2867                         h->sq[i].x, h->sq[i].y,\r
2868                         pen);\r
2869   }\r
2870 }\r
2871 \r
2872 /* Note: sqcolor is used only in monoMode */\r
2873 /* Note that this code is largely duplicated in woptions.c,\r
2874    function DrawSampleSquare, so that needs to be updated too */\r
2875 VOID\r
2876 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2877 {\r
2878   HBITMAP oldBitmap;\r
2879   HBRUSH oldBrush;\r
2880   int tmpSize;\r
2881 \r
2882   if (appData.blindfold) return;\r
2883 \r
2884   /* [AS] Use font-based pieces if needed */\r
2885   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2886     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2887     CreatePiecesFromFont();\r
2888 \r
2889     if( fontBitmapSquareSize == squareSize ) {\r
2890         int index = TranslatePieceToFontPiece(piece);\r
2891 \r
2892         SelectObject( tmphdc, hPieceMask[ index ] );\r
2893 \r
2894       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2895         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2896       else\r
2897         BitBlt( hdc,\r
2898             x, y,\r
2899             squareSize, squareSize,\r
2900             tmphdc,\r
2901             0, 0,\r
2902             SRCAND );\r
2903 \r
2904         SelectObject( tmphdc, hPieceFace[ index ] );\r
2905 \r
2906       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2907         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2908       else\r
2909         BitBlt( hdc,\r
2910             x, y,\r
2911             squareSize, squareSize,\r
2912             tmphdc,\r
2913             0, 0,\r
2914             SRCPAINT );\r
2915 \r
2916         return;\r
2917     }\r
2918   }\r
2919 \r
2920   if (appData.monoMode) {\r
2921     SelectObject(tmphdc, PieceBitmap(piece, \r
2922       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2923     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2924            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2925   } else {\r
2926     HBRUSH xBrush = whitePieceBrush;\r
2927     tmpSize = squareSize;\r
2928     if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);\r
2929     if(minorSize &&\r
2930         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2931          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2932       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2933       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2934       x += (squareSize - minorSize)>>1;\r
2935       y += squareSize - minorSize - 2;\r
2936       tmpSize = minorSize;\r
2937     }\r
2938     if (color || appData.allWhite ) {\r
2939       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2940       if( color )\r
2941               oldBrush = SelectObject(hdc, xBrush);\r
2942       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2943       if(appData.upsideDown && color==flipView)\r
2944         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2945       else\r
2946         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2947       /* Use black for outline of white pieces */\r
2948       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2949       if(appData.upsideDown && color==flipView)\r
2950         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2951       else\r
2952         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2953     } else if(appData.pieceDirectory[0]) {\r
2954       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2955       oldBrush = SelectObject(hdc, xBrush);\r
2956       if(appData.upsideDown && color==flipView)\r
2957         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2958       else\r
2959         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2960       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2961       if(appData.upsideDown && color==flipView)\r
2962         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2963       else\r
2964         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2965     } else {\r
2966       /* Use square color for details of black pieces */\r
2967       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2968       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2969       if(appData.upsideDown && !flipView)\r
2970         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2971       else\r
2972         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2973     }\r
2974     SelectObject(hdc, oldBrush);\r
2975     SelectObject(tmphdc, oldBitmap);\r
2976   }\r
2977 }\r
2978 \r
2979 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2980 int GetBackTextureMode( int algo )\r
2981 {\r
2982     int result = BACK_TEXTURE_MODE_DISABLED;\r
2983 \r
2984     switch( algo ) \r
2985     {\r
2986         case BACK_TEXTURE_MODE_PLAIN:\r
2987             result = 1; /* Always use identity map */\r
2988             break;\r
2989         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2990             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2991             break;\r
2992     }\r
2993 \r
2994     return result;\r
2995 }\r
2996 \r
2997 /* \r
2998     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2999     to handle redraws cleanly (as random numbers would always be different).\r
3000 */\r
3001 VOID RebuildTextureSquareInfo()\r
3002 {\r
3003     BITMAP bi;\r
3004     int lite_w = 0;\r
3005     int lite_h = 0;\r
3006     int dark_w = 0;\r
3007     int dark_h = 0;\r
3008     int row;\r
3009     int col;\r
3010 \r
3011     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3012 \r
3013     if( liteBackTexture != NULL ) {\r
3014         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3015             lite_w = bi.bmWidth;\r
3016             lite_h = bi.bmHeight;\r
3017         }\r
3018     }\r
3019 \r
3020     if( darkBackTexture != NULL ) {\r
3021         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3022             dark_w = bi.bmWidth;\r
3023             dark_h = bi.bmHeight;\r
3024         }\r
3025     }\r
3026 \r
3027     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3028         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3029             if( (col + row) & 1 ) {\r
3030                 /* Lite square */\r
3031                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3032                   if( lite_w >= squareSize*BOARD_WIDTH )\r
3033                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
3034                   else\r
3035                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3036                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
3037                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
3038                   else\r
3039                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3040                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3041                 }\r
3042             }\r
3043             else {\r
3044                 /* Dark square */\r
3045                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3046                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3047                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3048                   else\r
3049                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3050                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3051                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3052                   else\r
3053                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3054                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3055                 }\r
3056             }\r
3057         }\r
3058     }\r
3059 }\r
3060 \r
3061 /* [AS] Arrow highlighting support */\r
3062 \r
3063 static double A_WIDTH = 5; /* Width of arrow body */\r
3064 \r
3065 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3066 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3067 \r
3068 static double Sqr( double x )\r
3069 {\r
3070     return x*x;\r
3071 }\r
3072 \r
3073 static int Round( double x )\r
3074 {\r
3075     return (int) (x + 0.5);\r
3076 }\r
3077 \r
3078 /* Draw an arrow between two points using current settings */\r
3079 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3080 {\r
3081     POINT arrow[7];\r
3082     double dx, dy, j, k, x, y;\r
3083 \r
3084     if( d_x == s_x ) {\r
3085         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3086 \r
3087         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3088         arrow[0].y = s_y;\r
3089 \r
3090         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3091         arrow[1].y = d_y - h;\r
3092 \r
3093         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3094         arrow[2].y = d_y - h;\r
3095 \r
3096         arrow[3].x = d_x;\r
3097         arrow[3].y = d_y;\r
3098 \r
3099         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3100         arrow[5].y = d_y - h;\r
3101 \r
3102         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3103         arrow[4].y = d_y - h;\r
3104 \r
3105         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3106         arrow[6].y = s_y;\r
3107     }\r
3108     else if( d_y == s_y ) {\r
3109         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3110 \r
3111         arrow[0].x = s_x;\r
3112         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3113 \r
3114         arrow[1].x = d_x - w;\r
3115         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3116 \r
3117         arrow[2].x = d_x - w;\r
3118         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3119 \r
3120         arrow[3].x = d_x;\r
3121         arrow[3].y = d_y;\r
3122 \r
3123         arrow[5].x = d_x - w;\r
3124         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3125 \r
3126         arrow[4].x = d_x - w;\r
3127         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3128 \r
3129         arrow[6].x = s_x;\r
3130         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3131     }\r
3132     else {\r
3133         /* [AS] Needed a lot of paper for this! :-) */\r
3134         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3135         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3136   \r
3137         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3138 \r
3139         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3140 \r
3141         x = s_x;\r
3142         y = s_y;\r
3143 \r
3144         arrow[0].x = Round(x - j);\r
3145         arrow[0].y = Round(y + j*dx);\r
3146 \r
3147         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3148         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3149 \r
3150         if( d_x > s_x ) {\r
3151             x = (double) d_x - k;\r
3152             y = (double) d_y - k*dy;\r
3153         }\r
3154         else {\r
3155             x = (double) d_x + k;\r
3156             y = (double) d_y + k*dy;\r
3157         }\r
3158 \r
3159         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3160 \r
3161         arrow[6].x = Round(x - j);\r
3162         arrow[6].y = Round(y + j*dx);\r
3163 \r
3164         arrow[2].x = Round(arrow[6].x + 2*j);\r
3165         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3166 \r
3167         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3168         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3169 \r
3170         arrow[4].x = d_x;\r
3171         arrow[4].y = d_y;\r
3172 \r
3173         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3174         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3175     }\r
3176 \r
3177     Polygon( hdc, arrow, 7 );\r
3178 }\r
3179 \r
3180 /* [AS] Draw an arrow between two squares */\r
3181 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3182 {\r
3183     int s_x, s_y, d_x, d_y;\r
3184     HPEN hpen;\r
3185     HPEN holdpen;\r
3186     HBRUSH hbrush;\r
3187     HBRUSH holdbrush;\r
3188     LOGBRUSH stLB;\r
3189 \r
3190     if( s_col == d_col && s_row == d_row ) {\r
3191         return;\r
3192     }\r
3193 \r
3194     /* Get source and destination points */\r
3195     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3196     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3197 \r
3198     if( d_y > s_y ) {\r
3199         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3200     }\r
3201     else if( d_y < s_y ) {\r
3202         d_y += squareSize / 2 + squareSize / 4;\r
3203     }\r
3204     else {\r
3205         d_y += squareSize / 2;\r
3206     }\r
3207 \r
3208     if( d_x > s_x ) {\r
3209         d_x += squareSize / 2 - squareSize / 4;\r
3210     }\r
3211     else if( d_x < s_x ) {\r
3212         d_x += squareSize / 2 + squareSize / 4;\r
3213     }\r
3214     else {\r
3215         d_x += squareSize / 2;\r
3216     }\r
3217 \r
3218     s_x += squareSize / 2;\r
3219     s_y += squareSize / 2;\r
3220 \r
3221     /* Adjust width */\r
3222     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3223 \r
3224     /* Draw */\r
3225     stLB.lbStyle = BS_SOLID;\r
3226     stLB.lbColor = appData.highlightArrowColor;\r
3227     stLB.lbHatch = 0;\r
3228 \r
3229     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3230     holdpen = SelectObject( hdc, hpen );\r
3231     hbrush = CreateBrushIndirect( &stLB );\r
3232     holdbrush = SelectObject( hdc, hbrush );\r
3233 \r
3234     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3235 \r
3236     SelectObject( hdc, holdpen );\r
3237     SelectObject( hdc, holdbrush );\r
3238     DeleteObject( hpen );\r
3239     DeleteObject( hbrush );\r
3240 }\r
3241 \r
3242 BOOL HasHighlightInfo()\r
3243 {\r
3244     BOOL result = FALSE;\r
3245 \r
3246     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3247         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3248     {\r
3249         result = TRUE;\r
3250     }\r
3251 \r
3252     return result;\r
3253 \r
3254 }\r
3255 \r
3256 BOOL IsDrawArrowEnabled()\r
3257 {\r
3258     BOOL result = FALSE;\r
3259 \r
3260     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3261         result = TRUE;\r
3262     }\r
3263 \r
3264     return result;\r
3265 }\r
3266 \r
3267 VOID DrawArrowHighlight( HDC hdc )\r
3268 {\r
3269     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3270         DrawArrowBetweenSquares( hdc,\r
3271             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3272             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3273     }\r
3274 }\r
3275 \r
3276 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3277 {\r
3278     HRGN result = NULL;\r
3279 \r
3280     if( HasHighlightInfo() ) {\r
3281         int x1, y1, x2, y2;\r
3282         int sx, sy, dx, dy;\r
3283 \r
3284         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3285         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3286 \r
3287         sx = MIN( x1, x2 );\r
3288         sy = MIN( y1, y2 );\r
3289         dx = MAX( x1, x2 ) + squareSize;\r
3290         dy = MAX( y1, y2 ) + squareSize;\r
3291 \r
3292         result = CreateRectRgn( sx, sy, dx, dy );\r
3293     }\r
3294 \r
3295     return result;\r
3296 }\r
3297 \r
3298 /*\r
3299     Warning: this function modifies the behavior of several other functions. \r
3300     \r
3301     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3302     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3303     repaint is scattered all over the place, which is not good for features such as\r
3304     "arrow highlighting" that require a full repaint of the board.\r
3305 \r
3306     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3307     user interaction, when speed is not so important) but especially to avoid errors\r
3308     in the displayed graphics.\r
3309 \r
3310     In such patched places, I always try refer to this function so there is a single\r
3311     place to maintain knowledge.\r
3312     \r
3313     To restore the original behavior, just return FALSE unconditionally.\r
3314 */\r
3315 BOOL IsFullRepaintPreferrable()\r
3316 {\r
3317     BOOL result = FALSE;\r
3318 \r
3319     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3320         /* Arrow may appear on the board */\r
3321         result = TRUE;\r
3322     }\r
3323 \r
3324     return result;\r
3325 }\r
3326 \r
3327 /* \r
3328     This function is called by DrawPosition to know whether a full repaint must\r
3329     be forced or not.\r
3330 \r
3331     Only DrawPosition may directly call this function, which makes use of \r
3332     some state information. Other function should call DrawPosition specifying \r
3333     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3334 */\r
3335 BOOL DrawPositionNeedsFullRepaint()\r
3336 {\r
3337     BOOL result = FALSE;\r
3338 \r
3339     /* \r
3340         Probably a slightly better policy would be to trigger a full repaint\r
3341         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3342         but animation is fast enough that it's difficult to notice.\r
3343     */\r
3344     if( animInfo.piece == EmptySquare ) {\r
3345         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3346             result = TRUE;\r
3347         }\r
3348     }\r
3349 \r
3350     return result;\r
3351 }\r
3352 \r
3353 static HBITMAP borderBitmap;\r
3354 \r
3355 VOID\r
3356 DrawBackgroundOnDC(HDC hdc)\r
3357 {\r
3358   \r
3359   BITMAP bi;\r
3360   HDC tmphdc;\r
3361   HBITMAP hbm;\r
3362   static char oldBorder[MSG_SIZ];\r
3363   int w = 600, h = 600, mode;\r
3364 \r
3365   if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid\r
3366     strncpy(oldBorder, appData.border, MSG_SIZ-1);\r
3367     borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
3368   }\r
3369   if(borderBitmap == NULL) { // loading failed, use white\r
3370     FillRect( hdc, &boardRect, whitePieceBrush );\r
3371     return;\r
3372   }\r
3373   tmphdc = CreateCompatibleDC(hdc);\r
3374   hbm = SelectObject(tmphdc, borderBitmap);\r
3375   if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {\r
3376             w = bi.bmWidth;\r
3377             h = bi.bmHeight;\r
3378   }\r
3379   mode = SetStretchBltMode(hdc, COLORONCOLOR);\r
3380   StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left, \r
3381                   boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3382   SetStretchBltMode(hdc, mode);\r
3383   SelectObject(tmphdc, hbm);\r
3384   DeleteDC(tmphdc);\r
3385 }\r
3386 \r
3387 VOID\r
3388 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3389 {\r
3390   int row, column, x, y, square_color, piece_color;\r
3391   ChessSquare piece;\r
3392   HBRUSH oldBrush;\r
3393   HDC texture_hdc = NULL;\r
3394 \r
3395   /* [AS] Initialize background textures if needed */\r
3396   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3397       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3398       if( backTextureSquareSize != squareSize \r
3399        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3400           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3401           backTextureSquareSize = squareSize;\r
3402           RebuildTextureSquareInfo();\r
3403       }\r
3404 \r
3405       texture_hdc = CreateCompatibleDC( hdc );\r
3406   }\r
3407 \r
3408   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3409     for (column = 0; column < BOARD_WIDTH; column++) {\r
3410   \r
3411       SquareToPos(row, column, &x, &y);\r
3412 \r
3413       piece = board[row][column];\r
3414 \r
3415       square_color = ((column + row) % 2) == 1;\r
3416       if( gameInfo.variant == VariantXiangqi ) {\r
3417           square_color = !InPalace(row, column);\r
3418           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3419           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3420       }\r
3421       piece_color = (int) piece < (int) BlackPawn;\r
3422 \r
3423 \r
3424       /* [HGM] holdings file: light square or black */\r
3425       if(column == BOARD_LEFT-2) {\r
3426             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3427                 square_color = 1;\r
3428             else {\r
3429                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3430                 continue;\r
3431             }\r
3432       } else\r
3433       if(column == BOARD_RGHT + 1 ) {\r
3434             if( row < gameInfo.holdingsSize )\r
3435                 square_color = 1;\r
3436             else {\r
3437                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3438                 continue;\r
3439             }\r
3440       }\r
3441       if(column == BOARD_LEFT-1 ) /* left align */\r
3442             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3443       else if( column == BOARD_RGHT) /* right align */\r
3444             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3445       else if( piece == DarkSquare) DisplayHoldingsCount(hdc, x, y, 0, 0);\r
3446       else\r
3447       if (appData.monoMode) {\r
3448         if (piece == EmptySquare) {\r
3449           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3450                  square_color ? WHITENESS : BLACKNESS);\r
3451         } else {\r
3452           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3453         }\r
3454       } \r
3455       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3456           /* [AS] Draw the square using a texture bitmap */\r
3457           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3458           int r = row, c = column; // [HGM] do not flip board in flipView\r
3459           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3460 \r
3461           DrawTile( x, y, \r
3462               squareSize, squareSize, \r
3463               hdc, \r
3464               texture_hdc,\r
3465               backTextureSquareInfo[r][c].mode,\r
3466               backTextureSquareInfo[r][c].x,\r
3467               backTextureSquareInfo[r][c].y );\r
3468 \r
3469           SelectObject( texture_hdc, hbm );\r
3470 \r
3471           if (piece != EmptySquare) {\r
3472               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3473           }\r
3474       }\r
3475       else {\r
3476         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3477 \r
3478         oldBrush = SelectObject(hdc, brush );\r
3479         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3480         SelectObject(hdc, oldBrush);\r
3481         if (piece != EmptySquare)\r
3482           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3483       }\r
3484     }\r
3485   }\r
3486 \r
3487   if( texture_hdc != NULL ) {\r
3488     DeleteDC( texture_hdc );\r
3489   }\r
3490 }\r
3491 \r
3492 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3493 void fputDW(FILE *f, int x)\r
3494 {\r
3495         fputc(x     & 255, f);\r
3496         fputc(x>>8  & 255, f);\r
3497         fputc(x>>16 & 255, f);\r
3498         fputc(x>>24 & 255, f);\r
3499 }\r
3500 \r
3501 #define MAX_CLIPS 200   /* more than enough */\r
3502 \r
3503 VOID\r
3504 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3505 {\r
3506 //  HBITMAP bufferBitmap;\r
3507   BITMAP bi;\r
3508 //  RECT Rect;\r
3509   HDC tmphdc;\r
3510   HBITMAP hbm;\r
3511   int w = 100, h = 50;\r
3512 \r
3513   if(logo == NULL) {\r
3514     if(!logoHeight) return;\r
3515     FillRect( hdc, &logoRect, whitePieceBrush );\r
3516   }\r
3517 //  GetClientRect(hwndMain, &Rect);\r
3518 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3519 //                                      Rect.bottom-Rect.top+1);\r
3520   tmphdc = CreateCompatibleDC(hdc);\r
3521   hbm = SelectObject(tmphdc, logo);\r
3522   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3523             w = bi.bmWidth;\r
3524             h = bi.bmHeight;\r
3525   }\r
3526   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3527                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3528   SelectObject(tmphdc, hbm);\r
3529   DeleteDC(tmphdc);\r
3530 }\r
3531 \r
3532 VOID\r
3533 DisplayLogos()\r
3534 {\r
3535   if(logoHeight) {\r
3536         HDC hdc = GetDC(hwndMain);\r
3537         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3538         if(appData.autoLogo) {\r
3539           \r
3540           switch(gameMode) { // pick logos based on game mode\r
3541             case IcsObserving:\r
3542                 whiteLogo = second.programLogo; // ICS logo\r
3543                 blackLogo = second.programLogo;\r
3544             default:\r
3545                 break;\r
3546             case IcsPlayingWhite:\r
3547                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3548                 blackLogo = second.programLogo; // ICS logo\r
3549                 break;\r
3550             case IcsPlayingBlack:\r
3551                 whiteLogo = second.programLogo; // ICS logo\r
3552                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3553                 break;\r
3554             case TwoMachinesPlay:\r
3555                 if(first.twoMachinesColor[0] == 'b') {\r
3556                     whiteLogo = second.programLogo;\r
3557                     blackLogo = first.programLogo;\r
3558                 }\r
3559                 break;\r
3560             case MachinePlaysWhite:\r
3561                 blackLogo = userLogo;\r
3562                 break;\r
3563             case MachinePlaysBlack:\r
3564                 whiteLogo = userLogo;\r
3565                 blackLogo = first.programLogo;\r
3566           }\r
3567         }\r
3568         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3569         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3570         ReleaseDC(hwndMain, hdc);\r
3571   }\r
3572 }\r
3573 \r
3574 void\r
3575 UpdateLogos(int display)\r
3576 { // called after loading new engine(s), in tourney or from menu\r
3577   LoadLogo(&first, 0, FALSE);\r
3578   LoadLogo(&second, 1, appData.icsActive);\r
3579   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3580   if(display) DisplayLogos();\r
3581 }\r
3582 \r
3583 static HDC hdcSeek;\r
3584 \r
3585 // [HGM] seekgraph\r
3586 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3587 {\r
3588     POINT stPt;\r
3589     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3590     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3591     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3592     SelectObject( hdcSeek, hp );\r
3593 }\r
3594 \r
3595 // front-end wrapper for drawing functions to do rectangles\r
3596 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3597 {\r
3598     HPEN hp;\r
3599     RECT rc;\r
3600 \r
3601     if (hdcSeek == NULL) {\r
3602     hdcSeek = GetDC(hwndMain);\r
3603       if (!appData.monoMode) {\r
3604         SelectPalette(hdcSeek, hPal, FALSE);\r
3605         RealizePalette(hdcSeek);\r
3606       }\r
3607     }\r
3608     hp = SelectObject( hdcSeek, gridPen );\r
3609     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3610     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3611     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3612     SelectObject( hdcSeek, hp );\r
3613 }\r
3614 \r
3615 // front-end wrapper for putting text in graph\r
3616 void DrawSeekText(char *buf, int x, int y)\r
3617 {\r
3618         SIZE stSize;\r
3619         SetBkMode( hdcSeek, TRANSPARENT );\r
3620         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3621         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3622 }\r
3623 \r
3624 void DrawSeekDot(int x, int y, int color)\r
3625 {\r
3626         int square = color & 0x80;\r
3627         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3628                         color == 0 ? markerBrush[1] : color == 1 ? darkSquareBrush : explodeBrush);\r
3629         color &= 0x7F;\r
3630         if(square)\r
3631             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3632                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3633         else\r
3634             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3635                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3636             SelectObject(hdcSeek, oldBrush);\r
3637 }\r
3638 \r
3639 void DrawSeekOpen()\r
3640 {\r
3641 }\r
3642 \r
3643 void DrawSeekClose()\r
3644 {\r
3645 }\r
3646 \r
3647 VOID\r
3648 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3649 {\r
3650   static Board lastReq[2], lastDrawn[2];\r
3651   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3652   static int lastDrawnFlipView = 0;\r
3653   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3654   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3655   HDC tmphdc;\r
3656   HDC hdcmem;\r
3657   HBITMAP bufferBitmap;\r
3658   HBITMAP oldBitmap;\r
3659   RECT Rect;\r
3660   HRGN clips[MAX_CLIPS];\r
3661   ChessSquare dragged_piece = EmptySquare;\r
3662   int nr = twoBoards*partnerUp;\r
3663 \r
3664   /* I'm undecided on this - this function figures out whether a full\r
3665    * repaint is necessary on its own, so there's no real reason to have the\r
3666    * caller tell it that.  I think this can safely be set to FALSE - but\r
3667    * if we trust the callers not to request full repaints unnessesarily, then\r
3668    * we could skip some clipping work.  In other words, only request a full\r
3669    * redraw when the majority of pieces have changed positions (ie. flip, \r
3670    * gamestart and similar)  --Hawk\r
3671    */\r
3672   Boolean fullrepaint = repaint;\r
3673 \r
3674   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3675 \r
3676   if( DrawPositionNeedsFullRepaint() ) {\r
3677       fullrepaint = TRUE;\r
3678   }\r
3679 \r
3680   if (board == NULL) {\r
3681     if (!lastReqValid[nr]) {\r
3682       return;\r
3683     }\r
3684     board = lastReq[nr];\r
3685   } else {\r
3686     CopyBoard(lastReq[nr], board);\r
3687     lastReqValid[nr] = 1;\r
3688   }\r
3689 \r
3690   if (doingSizing) {\r
3691     return;\r
3692   }\r
3693 \r
3694   if (IsIconic(hwndMain)) {\r
3695     return;\r
3696   }\r
3697 \r
3698   if (hdc == NULL) {\r
3699     hdc = GetDC(hwndMain);\r
3700     if (!appData.monoMode) {\r
3701       SelectPalette(hdc, hPal, FALSE);\r
3702       RealizePalette(hdc);\r
3703     }\r
3704     releaseDC = TRUE;\r
3705   } else {\r
3706     releaseDC = FALSE;\r
3707   }\r
3708 \r
3709   /* Create some work-DCs */\r
3710   hdcmem = CreateCompatibleDC(hdc);\r
3711   tmphdc = CreateCompatibleDC(hdc);\r
3712 \r
3713   /* If dragging is in progress, we temporarely remove the piece */\r
3714   /* [HGM] or temporarily decrease count if stacked              */\r
3715   /*       !! Moved to before board compare !!                   */\r
3716   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3717     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3718     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3719             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3720         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3721     } else \r
3722     if(dragInfo.from.x == BOARD_RGHT+1) {\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         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3727   }\r
3728 \r
3729   /* Figure out which squares need updating by comparing the \r
3730    * newest board with the last drawn board and checking if\r
3731    * flipping has changed.\r
3732    */\r
3733   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3734     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3735       for (column = 0; column < BOARD_WIDTH; column++) {\r
3736         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3737           SquareToPos(row, column, &x, &y);\r
3738           clips[num_clips++] =\r
3739             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3740         }\r
3741       }\r
3742     }\r
3743    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3744     for (i=0; i<2; i++) {\r
3745       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3746           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3747         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3748             lastDrawnHighlight.sq[i].y >= 0) {\r
3749           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3750                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3751           clips[num_clips++] =\r
3752             CreateRectRgn(x - lineGap, y - lineGap, \r
3753                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3754         }\r
3755         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3756           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3757           clips[num_clips++] =\r
3758             CreateRectRgn(x - lineGap, y - lineGap, \r
3759                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3760         }\r
3761       }\r
3762     }\r
3763     for (i=0; i<2; i++) {\r
3764       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3765           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3766         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3767             lastDrawnPremove.sq[i].y >= 0) {\r
3768           SquareToPos(lastDrawnPremove.sq[i].y,\r
3769                       lastDrawnPremove.sq[i].x, &x, &y);\r
3770           clips[num_clips++] =\r
3771             CreateRectRgn(x - lineGap, y - lineGap, \r
3772                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3773         }\r
3774         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3775             premoveHighlightInfo.sq[i].y >= 0) {\r
3776           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3777                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3778           clips[num_clips++] =\r
3779             CreateRectRgn(x - lineGap, y - lineGap, \r
3780                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3781         }\r
3782       }\r
3783     }\r
3784    } else { // nr == 1\r
3785         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3786         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3787         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3788         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3789       for (i=0; i<2; i++) {\r
3790         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3791             partnerHighlightInfo.sq[i].y >= 0) {\r
3792           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3793                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3794           clips[num_clips++] =\r
3795             CreateRectRgn(x - lineGap, y - lineGap, \r
3796                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3797         }\r
3798         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3799             oldPartnerHighlight.sq[i].y >= 0) {\r
3800           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3801                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3802           clips[num_clips++] =\r
3803             CreateRectRgn(x - lineGap, y - lineGap, \r
3804                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3805         }\r
3806       }\r
3807    }\r
3808   } else {\r
3809     fullrepaint = TRUE;\r
3810   }\r
3811 \r
3812   /* Create a buffer bitmap - this is the actual bitmap\r
3813    * being written to.  When all the work is done, we can\r
3814    * copy it to the real DC (the screen).  This avoids\r
3815    * the problems with flickering.\r
3816    */\r
3817   GetClientRect(hwndMain, &Rect);\r
3818   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3819                                         Rect.bottom-Rect.top+1);\r
3820   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3821   if (!appData.monoMode) {\r
3822     SelectPalette(hdcmem, hPal, FALSE);\r
3823   }\r
3824 \r
3825   /* Create clips for dragging */\r
3826   if (!fullrepaint) {\r
3827     if (dragInfo.from.x >= 0) {\r
3828       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3829       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3830     }\r
3831     if (dragInfo.start.x >= 0) {\r
3832       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3833       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3834     }\r
3835     if (dragInfo.pos.x >= 0) {\r
3836       x = dragInfo.pos.x - squareSize / 2;\r
3837       y = dragInfo.pos.y - squareSize / 2;\r
3838       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3839     }\r
3840     if (dragInfo.lastpos.x >= 0) {\r
3841       x = dragInfo.lastpos.x - squareSize / 2;\r
3842       y = dragInfo.lastpos.y - squareSize / 2;\r
3843       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3844     }\r
3845   }\r
3846 \r
3847   /* Are we animating a move?  \r
3848    * If so, \r
3849    *   - remove the piece from the board (temporarely)\r
3850    *   - calculate the clipping region\r
3851    */\r
3852   if (!fullrepaint) {\r
3853     if (animInfo.piece != EmptySquare) {\r
3854       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3855       x = boardRect.left + animInfo.lastpos.x;\r
3856       y = boardRect.top + animInfo.lastpos.y;\r
3857       x2 = boardRect.left + animInfo.pos.x;\r
3858       y2 = boardRect.top + animInfo.pos.y;\r
3859       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3860       /* Slight kludge.  The real problem is that after AnimateMove is\r
3861          done, the position on the screen does not match lastDrawn.\r
3862          This currently causes trouble only on e.p. captures in\r
3863          atomic, where the piece moves to an empty square and then\r
3864          explodes.  The old and new positions both had an empty square\r
3865          at the destination, but animation has drawn a piece there and\r
3866          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3867       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3868     }\r
3869   }\r
3870 \r
3871   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3872   if (num_clips == 0)\r
3873     fullrepaint = TRUE;\r
3874 \r
3875   /* Set clipping on the memory DC */\r
3876   if (!fullrepaint) {\r
3877     SelectClipRgn(hdcmem, clips[0]);\r
3878     for (x = 1; x < num_clips; x++) {\r
3879       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3880         abort();  // this should never ever happen!\r
3881     }\r
3882   }\r
3883 \r
3884   /* Do all the drawing to the memory DC */\r
3885   if(explodeInfo.radius) { // [HGM] atomic\r
3886         HBRUSH oldBrush;\r
3887         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3888         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3889         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3890         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3891         x += squareSize/2;\r
3892         y += squareSize/2;\r
3893         if(!fullrepaint) {\r
3894           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3895           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3896         }\r
3897         DrawGridOnDC(hdcmem);\r
3898         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3899         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3900         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3901         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3902         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3903         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3904         SelectObject(hdcmem, oldBrush);\r
3905   } else {\r
3906     if(border) DrawBackgroundOnDC(hdcmem);\r
3907     DrawGridOnDC(hdcmem);\r
3908     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3909         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3910         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3911     } else {\r
3912         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3913         oldPartnerHighlight = partnerHighlightInfo;\r
3914     }\r
3915     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3916   }\r
3917   if(nr == 0) // [HGM] dual: markers only on left board\r
3918   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3919     for (column = 0; column < BOARD_WIDTH; column++) {\r
3920         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3921             HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);\r
3922             SquareToPos(row, column, &x, &y);\r
3923             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3924                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3925             SelectObject(hdcmem, oldBrush);\r
3926         }\r
3927     }\r
3928   }\r
3929 \r
3930   if( appData.highlightMoveWithArrow ) {\r
3931     DrawArrowHighlight(hdcmem);\r
3932   }\r
3933 \r
3934   DrawCoordsOnDC(hdcmem);\r
3935 \r
3936   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3937                  /* to make sure lastDrawn contains what is actually drawn */\r
3938 \r
3939   /* Put the dragged piece back into place and draw it (out of place!) */\r
3940     if (dragged_piece != EmptySquare) {\r
3941     /* [HGM] or restack */\r
3942     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3943                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3944     else\r
3945     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3946                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3947     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3948     x = dragInfo.pos.x - squareSize / 2;\r
3949     y = dragInfo.pos.y - squareSize / 2;\r
3950     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
3951                   ((int) dragInfo.piece < (int) BlackPawn), \r
3952                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3953   }   \r
3954   \r
3955   /* Put the animated piece back into place and draw it */\r
3956   if (animInfo.piece != EmptySquare) {\r
3957     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3958     x = boardRect.left + animInfo.pos.x;\r
3959     y = boardRect.top + animInfo.pos.y;\r
3960     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3961                   ((int) animInfo.piece < (int) BlackPawn),\r
3962                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3963   }\r
3964 \r
3965   /* Release the bufferBitmap by selecting in the old bitmap \r
3966    * and delete the memory DC\r
3967    */\r
3968   SelectObject(hdcmem, oldBitmap);\r
3969   DeleteDC(hdcmem);\r
3970 \r
3971   /* Set clipping on the target DC */\r
3972   if (!fullrepaint) {\r
3973     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3974         RECT rect;\r
3975         GetRgnBox(clips[x], &rect);\r
3976         DeleteObject(clips[x]);\r
3977         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3978                           rect.right + wpMain.width/2, rect.bottom);\r
3979     }\r
3980     SelectClipRgn(hdc, clips[0]);\r
3981     for (x = 1; x < num_clips; x++) {\r
3982       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3983         abort();   // this should never ever happen!\r
3984     } \r
3985   }\r
3986 \r
3987   /* Copy the new bitmap onto the screen in one go.\r
3988    * This way we avoid any flickering\r
3989    */\r
3990   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3991   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3992          boardRect.right - boardRect.left,\r
3993          boardRect.bottom - boardRect.top,\r
3994          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3995   if(saveDiagFlag) { \r
3996     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
3997     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3998 \r
3999     GetObject(bufferBitmap, sizeof(b), &b);\r
4000     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
4001         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4002         bih.biWidth = b.bmWidth;\r
4003         bih.biHeight = b.bmHeight;\r
4004         bih.biPlanes = 1;\r
4005         bih.biBitCount = b.bmBitsPixel;\r
4006         bih.biCompression = 0;\r
4007         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4008         bih.biXPelsPerMeter = 0;\r
4009         bih.biYPelsPerMeter = 0;\r
4010         bih.biClrUsed = 0;\r
4011         bih.biClrImportant = 0;\r
4012 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4013 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4014         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4015 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4016 \r
4017         wb = b.bmWidthBytes;\r
4018         // count colors\r
4019         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4020                 int k = ((int*) pData)[i];\r
4021                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4022                 if(j >= 16) break;\r
4023                 color[j] = k;\r
4024                 if(j >= nrColors) nrColors = j+1;\r
4025         }\r
4026         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4027                 INT p = 0;\r
4028                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4029                     for(w=0; w<(wb>>2); w+=2) {\r
4030                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4031                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4032                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4033                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4034                         pData[p++] = m | j<<4;\r
4035                     }\r
4036                     while(p&3) pData[p++] = 0;\r
4037                 }\r
4038                 fac = 3;\r
4039                 wb = ((wb+31)>>5)<<2;\r
4040         }\r
4041         // write BITMAPFILEHEADER\r
4042         fprintf(diagFile, "BM");\r
4043         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4044         fputDW(diagFile, 0);\r
4045         fputDW(diagFile, 0x36 + (fac?64:0));\r
4046         // write BITMAPINFOHEADER\r
4047         fputDW(diagFile, 40);\r
4048         fputDW(diagFile, b.bmWidth);\r
4049         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4050         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4051         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4052         fputDW(diagFile, 0);\r
4053         fputDW(diagFile, 0);\r
4054         fputDW(diagFile, 0);\r
4055         fputDW(diagFile, 0);\r
4056         fputDW(diagFile, 0);\r
4057         fputDW(diagFile, 0);\r
4058         // write color table\r
4059         if(fac)\r
4060         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4061         // write bitmap data\r
4062         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4063                 fputc(pData[i], diagFile);\r
4064         free(pData);\r
4065      }\r
4066   }\r
4067 \r
4068   SelectObject(tmphdc, oldBitmap);\r
4069 \r
4070   /* Massive cleanup */\r
4071   for (x = 0; x < num_clips; x++)\r
4072     DeleteObject(clips[x]);\r
4073 \r
4074   DeleteDC(tmphdc);\r
4075   DeleteObject(bufferBitmap);\r
4076 \r
4077   if (releaseDC) \r
4078     ReleaseDC(hwndMain, hdc);\r
4079   \r
4080   if (lastDrawnFlipView != flipView && nr == 0) {\r
4081     if (flipView)\r
4082       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4083     else\r
4084       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4085   }\r
4086 \r
4087 /*  CopyBoard(lastDrawn, board);*/\r
4088   lastDrawnHighlight = highlightInfo;\r
4089   lastDrawnPremove   = premoveHighlightInfo;\r
4090   lastDrawnFlipView = flipView;\r
4091   lastDrawnValid[nr] = 1;\r
4092 }\r
4093 \r
4094 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4095 int\r
4096 SaveDiagram(f)\r
4097      FILE *f;\r
4098 {\r
4099     saveDiagFlag = 1; diagFile = f;\r
4100     HDCDrawPosition(NULL, TRUE, NULL);\r
4101     saveDiagFlag = 0;\r
4102 \r
4103     fclose(f);\r
4104     return TRUE;\r
4105 }\r
4106 \r
4107 \r
4108 /*---------------------------------------------------------------------------*\\r
4109 | CLIENT PAINT PROCEDURE\r
4110 |   This is the main event-handler for the WM_PAINT message.\r
4111 |\r
4112 \*---------------------------------------------------------------------------*/\r
4113 VOID\r
4114 PaintProc(HWND hwnd)\r
4115 {\r
4116   HDC         hdc;\r
4117   PAINTSTRUCT ps;\r
4118   HFONT       oldFont;\r
4119 \r
4120   if((hdc = BeginPaint(hwnd, &ps))) {\r
4121     if (IsIconic(hwnd)) {\r
4122       DrawIcon(hdc, 2, 2, iconCurrent);\r
4123     } else {\r
4124       if (!appData.monoMode) {\r
4125         SelectPalette(hdc, hPal, FALSE);\r
4126         RealizePalette(hdc);\r
4127       }\r
4128       HDCDrawPosition(hdc, 1, NULL);\r
4129       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4130         flipView = !flipView; partnerUp = !partnerUp;\r
4131         HDCDrawPosition(hdc, 1, NULL);\r
4132         flipView = !flipView; partnerUp = !partnerUp;\r
4133       }\r
4134       oldFont =\r
4135         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4136       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4137                  ETO_CLIPPED|ETO_OPAQUE,\r
4138                  &messageRect, messageText, strlen(messageText), NULL);\r
4139       SelectObject(hdc, oldFont);\r
4140       DisplayBothClocks();\r
4141       DisplayLogos();\r
4142     }\r
4143     EndPaint(hwnd,&ps);\r
4144   }\r
4145 \r
4146   return;\r
4147 }\r
4148 \r
4149 \r
4150 /*\r
4151  * If the user selects on a border boundary, return -1; if off the board,\r
4152  *   return -2.  Otherwise map the event coordinate to the square.\r
4153  * The offset boardRect.left or boardRect.top must already have been\r
4154  *   subtracted from x.\r
4155  */\r
4156 int EventToSquare(x, limit)\r
4157      int x, limit;\r
4158 {\r
4159   if (x <= border)\r
4160     return -2;\r
4161   if (x < lineGap + border)\r
4162     return -1;\r
4163   x -= lineGap + border;\r
4164   if ((x % (squareSize + lineGap)) >= squareSize)\r
4165     return -1;\r
4166   x /= (squareSize + lineGap);\r
4167     if (x >= limit)\r
4168     return -2;\r
4169   return x;\r
4170 }\r
4171 \r
4172 typedef struct {\r
4173   char piece;\r
4174   int command;\r
4175   char* name;\r
4176 } DropEnable;\r
4177 \r
4178 DropEnable dropEnables[] = {\r
4179   { 'P', DP_Pawn, N_("Pawn") },\r
4180   { 'N', DP_Knight, N_("Knight") },\r
4181   { 'B', DP_Bishop, N_("Bishop") },\r
4182   { 'R', DP_Rook, N_("Rook") },\r
4183   { 'Q', DP_Queen, N_("Queen") },\r
4184 };\r
4185 \r
4186 VOID\r
4187 SetupDropMenu(HMENU hmenu)\r
4188 {\r
4189   int i, count, enable;\r
4190   char *p;\r
4191   extern char white_holding[], black_holding[];\r
4192   char item[MSG_SIZ];\r
4193 \r
4194   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4195     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4196                dropEnables[i].piece);\r
4197     count = 0;\r
4198     while (p && *p++ == dropEnables[i].piece) count++;\r
4199       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4200     enable = count > 0 || !appData.testLegality\r
4201       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4202                       && !appData.icsActive);\r
4203     ModifyMenu(hmenu, dropEnables[i].command,\r
4204                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4205                dropEnables[i].command, item);\r
4206   }\r
4207 }\r
4208 \r
4209 void DragPieceBegin(int x, int y, Boolean instantly)\r
4210 {\r
4211       dragInfo.lastpos.x = boardRect.left + x;\r
4212       dragInfo.lastpos.y = boardRect.top + y;\r
4213       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4214       dragInfo.from.x = fromX;\r
4215       dragInfo.from.y = fromY;\r
4216       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4217       dragInfo.start = dragInfo.from;\r
4218       SetCapture(hwndMain);\r
4219 }\r
4220 \r
4221 void DragPieceEnd(int x, int y)\r
4222 {\r
4223     ReleaseCapture();\r
4224     dragInfo.start.x = dragInfo.start.y = -1;\r
4225     dragInfo.from = dragInfo.start;\r
4226     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4227 }\r
4228 \r
4229 void ChangeDragPiece(ChessSquare piece)\r
4230 {\r
4231     dragInfo.piece = piece;\r
4232 }\r
4233 \r
4234 /* Event handler for mouse messages */\r
4235 VOID\r
4236 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4237 {\r
4238   int x, y, menuNr;\r
4239   POINT pt;\r
4240   static int recursive = 0;\r
4241   HMENU hmenu;\r
4242   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4243 \r
4244   if (recursive) {\r
4245     if (message == WM_MBUTTONUP) {\r
4246       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4247          to the middle button: we simulate pressing the left button too!\r
4248          */\r
4249       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4250       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4251     }\r
4252     return;\r
4253   }\r
4254   recursive++;\r
4255   \r
4256   pt.x = LOWORD(lParam);\r
4257   pt.y = HIWORD(lParam);\r
4258   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4259   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4260   if (!flipView && y >= 0) {\r
4261     y = BOARD_HEIGHT - 1 - y;\r
4262   }\r
4263   if (flipView && x >= 0) {\r
4264     x = BOARD_WIDTH - 1 - x;\r
4265   }\r
4266 \r
4267   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4268   controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status\r
4269 \r
4270   switch (message) {\r
4271   case WM_LBUTTONDOWN:\r
4272       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4273         ClockClick(flipClock); break;\r
4274       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4275         ClockClick(!flipClock); break;\r
4276       }\r
4277       dragInfo.start.x = dragInfo.start.y = -1;\r
4278       dragInfo.from = dragInfo.start;\r
4279     if(fromX == -1 && frozen) { // not sure where this is for\r
4280                 fromX = fromY = -1; \r
4281       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4282       break;\r
4283     }\r
4284       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4285       DrawPosition(TRUE, NULL);\r
4286     break;\r
4287 \r
4288   case WM_LBUTTONUP:\r
4289       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4290       DrawPosition(TRUE, NULL);\r
4291     break;\r
4292 \r
4293   case WM_MOUSEMOVE:\r
4294     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4295     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4296     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4297     if ((appData.animateDragging || appData.highlightDragging)\r
4298         && (wParam & MK_LBUTTON)\r
4299         && dragInfo.from.x >= 0) \r
4300     {\r
4301       BOOL full_repaint = FALSE;\r
4302 \r
4303       if (appData.animateDragging) {\r
4304         dragInfo.pos = pt;\r
4305       }\r
4306       if (appData.highlightDragging) {\r
4307         HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);\r
4308         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4309             full_repaint = TRUE;\r
4310         }\r
4311       }\r
4312       \r
4313       DrawPosition( full_repaint, NULL);\r
4314       \r
4315       dragInfo.lastpos = dragInfo.pos;\r
4316     }\r
4317     break;\r
4318 \r
4319   case WM_MOUSEWHEEL: // [DM]\r
4320     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4321        /* Mouse Wheel is being rolled forward\r
4322         * Play moves forward\r
4323         */\r
4324        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4325                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4326        /* Mouse Wheel is being rolled backward\r
4327         * Play moves backward\r
4328         */\r
4329        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4330                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4331     }\r
4332     break;\r
4333 \r
4334   case WM_MBUTTONUP:\r
4335   case WM_RBUTTONUP:\r
4336     ReleaseCapture();\r
4337     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4338     break;\r
4339  \r
4340   case WM_MBUTTONDOWN:\r
4341   case WM_RBUTTONDOWN:\r
4342     ErrorPopDown();\r
4343     ReleaseCapture();\r
4344     fromX = fromY = -1;\r
4345     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4346     dragInfo.start.x = dragInfo.start.y = -1;\r
4347     dragInfo.from = dragInfo.start;\r
4348     dragInfo.lastpos = dragInfo.pos;\r
4349     if (appData.highlightDragging) {\r
4350       ClearHighlights();\r
4351     }\r
4352     if(y == -2) {\r
4353       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4354       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4355           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4356       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4357           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4358       }\r
4359       break;\r
4360     }\r
4361     DrawPosition(TRUE, NULL);\r
4362 \r
4363     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4364     switch (menuNr) {\r
4365     case 0:\r
4366       if (message == WM_MBUTTONDOWN) {\r
4367         buttonCount = 3;  /* even if system didn't think so */\r
4368         if (wParam & MK_SHIFT) \r
4369           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4370         else\r
4371           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4372       } else { /* message == WM_RBUTTONDOWN */\r
4373         /* Just have one menu, on the right button.  Windows users don't\r
4374            think to try the middle one, and sometimes other software steals\r
4375            it, or it doesn't really exist. */\r
4376         if(gameInfo.variant != VariantShogi)\r
4377             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4378         else\r
4379             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4380       }\r
4381       break;\r
4382     case 2:\r
4383       SetCapture(hwndMain);\r
4384       break;\r
4385     case 1:\r
4386       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4387       SetupDropMenu(hmenu);\r
4388       MenuPopup(hwnd, pt, hmenu, -1);\r
4389     default:\r
4390       break;\r
4391     }\r
4392     break;\r
4393   }\r
4394 \r
4395   recursive--;\r
4396 }\r
4397 \r
4398 /* Preprocess messages for buttons in main window */\r
4399 LRESULT CALLBACK\r
4400 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4401 {\r
4402   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4403   int i, dir;\r
4404 \r
4405   for (i=0; i<N_BUTTONS; i++) {\r
4406     if (buttonDesc[i].id == id) break;\r
4407   }\r
4408   if (i == N_BUTTONS) return 0;\r
4409   switch (message) {\r
4410   case WM_KEYDOWN:\r
4411     switch (wParam) {\r
4412     case VK_LEFT:\r
4413     case VK_RIGHT:\r
4414       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4415       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4416       return TRUE;\r
4417     }\r
4418     break;\r
4419   case WM_CHAR:\r
4420     switch (wParam) {\r
4421     case '\r':\r
4422       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4423       return TRUE;\r
4424     default:\r
4425       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4426         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4427         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4428         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4429         SetFocus(h);\r
4430         SendMessage(h, WM_CHAR, wParam, lParam);\r
4431         return TRUE;\r
4432       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4433         TypeInEvent((char)wParam);\r
4434       }\r
4435       break;\r
4436     }\r
4437     break;\r
4438   }\r
4439   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4440 }\r
4441 \r
4442 /* Process messages for Promotion dialog box */\r
4443 LRESULT CALLBACK\r
4444 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4445 {\r
4446   char promoChar;\r
4447 \r
4448   switch (message) {\r
4449   case WM_INITDIALOG: /* message: initialize dialog box */\r
4450     /* Center the dialog over the application window */\r
4451     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4452     Translate(hDlg, DLG_PromotionKing);\r
4453     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4454       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4455        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4456        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4457                SW_SHOW : SW_HIDE);\r
4458     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4459     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4460        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4461          PieceToChar(WhiteAngel) != '~') ||\r
4462         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4463          PieceToChar(BlackAngel) != '~')   ) ?\r
4464                SW_SHOW : SW_HIDE);\r
4465     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4466        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4467          PieceToChar(WhiteMarshall) != '~') ||\r
4468         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4469          PieceToChar(BlackMarshall) != '~')   ) ?\r
4470                SW_SHOW : SW_HIDE);\r
4471     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4472     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4473        gameInfo.variant != VariantShogi ?\r
4474                SW_SHOW : SW_HIDE);\r
4475     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4476        gameInfo.variant != VariantShogi ?\r
4477                SW_SHOW : SW_HIDE);\r
4478     if(gameInfo.variant == VariantShogi) {\r
4479         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4480         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4481         SetWindowText(hDlg, "Promote?");\r
4482     }\r
4483     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4484        gameInfo.variant == VariantSuper ?\r
4485                SW_SHOW : SW_HIDE);\r
4486     return TRUE;\r
4487 \r
4488   case WM_COMMAND: /* message: received a command */\r
4489     switch (LOWORD(wParam)) {\r
4490     case IDCANCEL:\r
4491       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4492       ClearHighlights();\r
4493       DrawPosition(FALSE, NULL);\r
4494       return TRUE;\r
4495     case PB_King:\r
4496       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4497       break;\r
4498     case PB_Queen:\r
4499       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4500       break;\r
4501     case PB_Rook:\r
4502       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4503       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4504       break;\r
4505     case PB_Bishop:\r
4506       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4507       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4508       break;\r
4509     case PB_Chancellor:\r
4510       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4511       break;\r
4512     case PB_Archbishop:\r
4513       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4514       break;\r
4515     case PB_Knight:\r
4516       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4517       break;\r
4518     default:\r
4519       return FALSE;\r
4520     }\r
4521     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4522     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4523     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4524     fromX = fromY = -1;\r
4525     if (!appData.highlightLastMove) {\r
4526       ClearHighlights();\r
4527       DrawPosition(FALSE, NULL);\r
4528     }\r
4529     return TRUE;\r
4530   }\r
4531   return FALSE;\r
4532 }\r
4533 \r
4534 /* Pop up promotion dialog */\r
4535 VOID\r
4536 PromotionPopup(HWND hwnd)\r
4537 {\r
4538   FARPROC lpProc;\r
4539 \r
4540   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4541   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4542     hwnd, (DLGPROC)lpProc);\r
4543   FreeProcInstance(lpProc);\r
4544 }\r
4545 \r
4546 void\r
4547 PromotionPopUp()\r
4548 {\r
4549   DrawPosition(TRUE, NULL);\r
4550   PromotionPopup(hwndMain);\r
4551 }\r
4552 \r
4553 VOID\r
4554 LoadGameDialog(HWND hwnd, char* title)\r
4555 {\r
4556   UINT number = 0;\r
4557   FILE *f;\r
4558   char fileTitle[MSG_SIZ];\r
4559   f = OpenFileDialog(hwnd, "rb", "",\r
4560                      appData.oldSaveStyle ? "gam" : "pgn",\r
4561                      GAME_FILT,\r
4562                      title, &number, fileTitle, NULL);\r
4563   if (f != NULL) {\r
4564     cmailMsgLoaded = FALSE;\r
4565     if (number == 0) {\r
4566       int error = GameListBuild(f);\r
4567       if (error) {\r
4568         DisplayError(_("Cannot build game list"), error);\r
4569       } else if (!ListEmpty(&gameList) &&\r
4570                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4571         GameListPopUp(f, fileTitle);\r
4572         return;\r
4573       }\r
4574       GameListDestroy();\r
4575       number = 1;\r
4576     }\r
4577     LoadGame(f, number, fileTitle, FALSE);\r
4578   }\r
4579 }\r
4580 \r
4581 int get_term_width()\r
4582 {\r
4583     HDC hdc;\r
4584     TEXTMETRIC tm;\r
4585     RECT rc;\r
4586     HFONT hfont, hold_font;\r
4587     LOGFONT lf;\r
4588     HWND hText;\r
4589 \r
4590     if (hwndConsole)\r
4591         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4592     else\r
4593         return 79;\r
4594 \r
4595     // get the text metrics\r
4596     hdc = GetDC(hText);\r
4597     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4598     if (consoleCF.dwEffects & CFE_BOLD)\r
4599         lf.lfWeight = FW_BOLD;\r
4600     if (consoleCF.dwEffects & CFE_ITALIC)\r
4601         lf.lfItalic = TRUE;\r
4602     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4603         lf.lfStrikeOut = TRUE;\r
4604     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4605         lf.lfUnderline = TRUE;\r
4606     hfont = CreateFontIndirect(&lf);\r
4607     hold_font = SelectObject(hdc, hfont);\r
4608     GetTextMetrics(hdc, &tm);\r
4609     SelectObject(hdc, hold_font);\r
4610     DeleteObject(hfont);\r
4611     ReleaseDC(hText, hdc);\r
4612 \r
4613     // get the rectangle\r
4614     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4615 \r
4616     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4617 }\r
4618 \r
4619 void UpdateICSWidth(HWND hText)\r
4620 {\r
4621     LONG old_width, new_width;\r
4622 \r
4623     new_width = get_term_width(hText, FALSE);\r
4624     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4625     if (new_width != old_width)\r
4626     {\r
4627         ics_update_width(new_width);\r
4628         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4629     }\r
4630 }\r
4631 \r
4632 VOID\r
4633 ChangedConsoleFont()\r
4634 {\r
4635   CHARFORMAT cfmt;\r
4636   CHARRANGE tmpsel, sel;\r
4637   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4638   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4639   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4640   PARAFORMAT paraf;\r
4641 \r
4642   cfmt.cbSize = sizeof(CHARFORMAT);\r
4643   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4644     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4645                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4646   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4647    * size.  This was undocumented in the version of MSVC++ that I had\r
4648    * when I wrote the code, but is apparently documented now.\r
4649    */\r
4650   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4651   cfmt.bCharSet = f->lf.lfCharSet;\r
4652   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4653   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4654   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4655   /* Why are the following seemingly needed too? */\r
4656   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4657   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4658   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4659   tmpsel.cpMin = 0;\r
4660   tmpsel.cpMax = -1; /*999999?*/\r
4661   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4662   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4663   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4664    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4665    */\r
4666   paraf.cbSize = sizeof(paraf);\r
4667   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4668   paraf.dxStartIndent = 0;\r
4669   paraf.dxOffset = WRAP_INDENT;\r
4670   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4671   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4672   UpdateICSWidth(hText);\r
4673 }\r
4674 \r
4675 /*---------------------------------------------------------------------------*\\r
4676  *\r
4677  * Window Proc for main window\r
4678  *\r
4679 \*---------------------------------------------------------------------------*/\r
4680 \r
4681 /* Process messages for main window, etc. */\r
4682 LRESULT CALLBACK\r
4683 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4684 {\r
4685   FARPROC lpProc;\r
4686   int wmId, wmEvent;\r
4687   char *defName;\r
4688   FILE *f;\r
4689   UINT number;\r
4690   char fileTitle[MSG_SIZ];\r
4691   static SnapData sd;\r
4692   static int peek=0;\r
4693 \r
4694   switch (message) {\r
4695 \r
4696   case WM_PAINT: /* message: repaint portion of window */\r
4697     PaintProc(hwnd);\r
4698     break;\r
4699 \r
4700   case WM_ERASEBKGND:\r
4701     if (IsIconic(hwnd)) {\r
4702       /* Cheat; change the message */\r
4703       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4704     } else {\r
4705       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4706     }\r
4707     break;\r
4708 \r
4709   case WM_LBUTTONDOWN:\r
4710   case WM_MBUTTONDOWN:\r
4711   case WM_RBUTTONDOWN:\r
4712   case WM_LBUTTONUP:\r
4713   case WM_MBUTTONUP:\r
4714   case WM_RBUTTONUP:\r
4715   case WM_MOUSEMOVE:\r
4716   case WM_MOUSEWHEEL:\r
4717     MouseEvent(hwnd, message, wParam, lParam);\r
4718     break;\r
4719 \r
4720   case WM_KEYUP:\r
4721     if((char)wParam == '\b') {\r
4722       ForwardEvent(); peek = 0;\r
4723     }\r
4724 \r
4725     JAWS_KBUP_NAVIGATION\r
4726 \r
4727     break;\r
4728 \r
4729   case WM_KEYDOWN:\r
4730     if((char)wParam == '\b') {\r
4731       if(!peek) BackwardEvent(), peek = 1;\r
4732     }\r
4733 \r
4734     JAWS_KBDOWN_NAVIGATION\r
4735 \r
4736     break;\r
4737 \r
4738   case WM_CHAR:\r
4739     \r
4740     JAWS_ALT_INTERCEPT\r
4741 \r
4742     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4743         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4744         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4745         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4746         SetFocus(h);\r
4747         SendMessage(h, message, wParam, lParam);\r
4748     } else if(lParam != KF_REPEAT) {\r
4749         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4750                 TypeInEvent((char)wParam);\r
4751         } else if((char)wParam == 003) CopyGameToClipboard();\r
4752          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4753     }\r
4754 \r
4755     break;\r
4756 \r
4757   case WM_PALETTECHANGED:\r
4758     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4759       int nnew;\r
4760       HDC hdc = GetDC(hwndMain);\r
4761       SelectPalette(hdc, hPal, TRUE);\r
4762       nnew = RealizePalette(hdc);\r
4763       if (nnew > 0) {\r
4764         paletteChanged = TRUE;\r
4765 \r
4766         InvalidateRect(hwnd, &boardRect, FALSE);\r
4767       }\r
4768       ReleaseDC(hwnd, hdc);\r
4769     }\r
4770     break;\r
4771 \r
4772   case WM_QUERYNEWPALETTE:\r
4773     if (!appData.monoMode /*&& paletteChanged*/) {\r
4774       int nnew;\r
4775       HDC hdc = GetDC(hwndMain);\r
4776       paletteChanged = FALSE;\r
4777       SelectPalette(hdc, hPal, FALSE);\r
4778       nnew = RealizePalette(hdc);\r
4779       if (nnew > 0) {\r
4780         InvalidateRect(hwnd, &boardRect, FALSE);\r
4781       }\r
4782       ReleaseDC(hwnd, hdc);\r
4783       return TRUE;\r
4784     }\r
4785     return FALSE;\r
4786 \r
4787   case WM_COMMAND: /* message: command from application menu */\r
4788     wmId    = LOWORD(wParam);\r
4789     wmEvent = HIWORD(wParam);\r
4790 \r
4791     switch (wmId) {\r
4792     case IDM_NewGame:\r
4793       ResetGameEvent();\r
4794       SAY("new game enter a move to play against the computer with white");\r
4795       break;\r
4796 \r
4797     case IDM_NewGameFRC:\r
4798       if( NewGameFRC() == 0 ) {\r
4799         ResetGameEvent();\r
4800       }\r
4801       break;\r
4802 \r
4803     case IDM_NewVariant:\r
4804       NewVariantPopup(hwnd);\r
4805       break;\r
4806 \r
4807     case IDM_LoadGame:\r
4808       LoadGameDialog(hwnd, _("Load Game from File"));\r
4809       break;\r
4810 \r
4811     case IDM_LoadNextGame:\r
4812       ReloadGame(1);\r
4813       break;\r
4814 \r
4815     case IDM_LoadPrevGame:\r
4816       ReloadGame(-1);\r
4817       break;\r
4818 \r
4819     case IDM_ReloadGame:\r
4820       ReloadGame(0);\r
4821       break;\r
4822 \r
4823     case IDM_LoadPosition:\r
4824       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4825         Reset(FALSE, TRUE);\r
4826       }\r
4827       number = 1;\r
4828       f = OpenFileDialog(hwnd, "rb", "",\r
4829                          appData.oldSaveStyle ? "pos" : "fen",\r
4830                          POSITION_FILT,\r
4831                          _("Load Position from File"), &number, fileTitle, NULL);\r
4832       if (f != NULL) {\r
4833         LoadPosition(f, number, fileTitle);\r
4834       }\r
4835       break;\r
4836 \r
4837     case IDM_LoadNextPosition:\r
4838       ReloadPosition(1);\r
4839       break;\r
4840 \r
4841     case IDM_LoadPrevPosition:\r
4842       ReloadPosition(-1);\r
4843       break;\r
4844 \r
4845     case IDM_ReloadPosition:\r
4846       ReloadPosition(0);\r
4847       break;\r
4848 \r
4849     case IDM_SaveGame:\r
4850       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4851       f = OpenFileDialog(hwnd, "a", defName,\r
4852                          appData.oldSaveStyle ? "gam" : "pgn",\r
4853                          GAME_FILT,\r
4854                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4855       if (f != NULL) {\r
4856         SaveGame(f, 0, "");\r
4857       }\r
4858       break;\r
4859 \r
4860     case IDM_SavePosition:\r
4861       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4862       f = OpenFileDialog(hwnd, "a", defName,\r
4863                          appData.oldSaveStyle ? "pos" : "fen",\r
4864                          POSITION_FILT,\r
4865                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4866       if (f != NULL) {\r
4867         SavePosition(f, 0, "");\r
4868       }\r
4869       break;\r
4870 \r
4871     case IDM_SaveDiagram:\r
4872       defName = "diagram";\r
4873       f = OpenFileDialog(hwnd, "wb", defName,\r
4874                          "bmp",\r
4875                          DIAGRAM_FILT,\r
4876                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4877       if (f != NULL) {\r
4878         SaveDiagram(f);\r
4879       }\r
4880       break;\r
4881 \r
4882     case IDM_CreateBook:\r
4883       CreateBookEvent();\r
4884       break;\r
4885 \r
4886     case IDM_CopyGame:\r
4887       CopyGameToClipboard();\r
4888       break;\r
4889 \r
4890     case IDM_PasteGame:\r
4891       PasteGameFromClipboard();\r
4892       break;\r
4893 \r
4894     case IDM_CopyGameListToClipboard:\r
4895       CopyGameListToClipboard();\r
4896       break;\r
4897 \r
4898     /* [AS] Autodetect FEN or PGN data */\r
4899     case IDM_PasteAny:\r
4900       PasteGameOrFENFromClipboard();\r
4901       break;\r
4902 \r
4903     /* [AS] Move history */\r
4904     case IDM_ShowMoveHistory:\r
4905         if( MoveHistoryIsUp() ) {\r
4906             MoveHistoryPopDown();\r
4907         }\r
4908         else {\r
4909             MoveHistoryPopUp();\r
4910         }\r
4911         break;\r
4912 \r
4913     /* [AS] Eval graph */\r
4914     case IDM_ShowEvalGraph:\r
4915         if( EvalGraphIsUp() ) {\r
4916             EvalGraphPopDown();\r
4917         }\r
4918         else {\r
4919             EvalGraphPopUp();\r
4920             SetFocus(hwndMain);\r
4921         }\r
4922         break;\r
4923 \r
4924     /* [AS] Engine output */\r
4925     case IDM_ShowEngineOutput:\r
4926         if( EngineOutputIsUp() ) {\r
4927             EngineOutputPopDown();\r
4928         }\r
4929         else {\r
4930             EngineOutputPopUp();\r
4931         }\r
4932         break;\r
4933 \r
4934     /* [AS] User adjudication */\r
4935     case IDM_UserAdjudication_White:\r
4936         UserAdjudicationEvent( +1 );\r
4937         break;\r
4938 \r
4939     case IDM_UserAdjudication_Black:\r
4940         UserAdjudicationEvent( -1 );\r
4941         break;\r
4942 \r
4943     case IDM_UserAdjudication_Draw:\r
4944         UserAdjudicationEvent( 0 );\r
4945         break;\r
4946 \r
4947     /* [AS] Game list options dialog */\r
4948     case IDM_GameListOptions:\r
4949       GameListOptions();\r
4950       break;\r
4951 \r
4952     case IDM_NewChat:\r
4953       ChatPopUp(NULL);\r
4954       break;\r
4955 \r
4956     case IDM_CopyPosition:\r
4957       CopyFENToClipboard();\r
4958       break;\r
4959 \r
4960     case IDM_PastePosition:\r
4961       PasteFENFromClipboard();\r
4962       break;\r
4963 \r
4964     case IDM_MailMove:\r
4965       MailMoveEvent();\r
4966       break;\r
4967 \r
4968     case IDM_ReloadCMailMsg:\r
4969       Reset(TRUE, TRUE);\r
4970       ReloadCmailMsgEvent(FALSE);\r
4971       break;\r
4972 \r
4973     case IDM_Minimize:\r
4974       ShowWindow(hwnd, SW_MINIMIZE);\r
4975       break;\r
4976 \r
4977     case IDM_Exit:\r
4978       ExitEvent(0);\r
4979       break;\r
4980 \r
4981     case IDM_MachineWhite:\r
4982       MachineWhiteEvent();\r
4983       /*\r
4984        * refresh the tags dialog only if it's visible\r
4985        */\r
4986       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4987           char *tags;\r
4988           tags = PGNTags(&gameInfo);\r
4989           TagsPopUp(tags, CmailMsg());\r
4990           free(tags);\r
4991       }\r
4992       SAY("computer starts playing white");\r
4993       break;\r
4994 \r
4995     case IDM_MachineBlack:\r
4996       MachineBlackEvent();\r
4997       /*\r
4998        * refresh the tags dialog only if it's visible\r
4999        */\r
5000       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5001           char *tags;\r
5002           tags = PGNTags(&gameInfo);\r
5003           TagsPopUp(tags, CmailMsg());\r
5004           free(tags);\r
5005       }\r
5006       SAY("computer starts playing black");\r
5007       break;\r
5008 \r
5009     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
5010       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
5011       break;\r
5012 \r
5013     case IDM_TwoMachines:\r
5014       TwoMachinesEvent();\r
5015       /*\r
5016        * refresh the tags dialog only if it's visible\r
5017        */\r
5018       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5019           char *tags;\r
5020           tags = PGNTags(&gameInfo);\r
5021           TagsPopUp(tags, CmailMsg());\r
5022           free(tags);\r
5023       }\r
5024       SAY("computer starts playing both sides");\r
5025       break;\r
5026 \r
5027     case IDM_AnalysisMode:\r
5028       if(AnalyzeModeEvent()) {\r
5029         SAY("analyzing current position");\r
5030       }\r
5031       break;\r
5032 \r
5033     case IDM_AnalyzeFile:\r
5034       AnalyzeFileEvent();\r
5035       break;\r
5036 \r
5037     case IDM_IcsClient:\r
5038       IcsClientEvent();\r
5039       break;\r
5040 \r
5041     case IDM_EditGame:\r
5042     case IDM_EditGame2:\r
5043       EditGameEvent();\r
5044       SAY("edit game");\r
5045       break;\r
5046 \r
5047     case IDM_EditPosition:\r
5048     case IDM_EditPosition2:\r
5049       EditPositionEvent();\r
5050       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
5051       break;\r
5052 \r
5053     case IDM_Training:\r
5054       TrainingEvent();\r
5055       break;\r
5056 \r
5057     case IDM_ShowGameList:\r
5058       ShowGameListProc();\r
5059       break;\r
5060 \r
5061     case IDM_EditProgs1:\r
5062       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
5063       break;\r
5064 \r
5065     case IDM_LoadProg1:\r
5066      LoadEnginePopUp(hwndMain, 0);\r
5067       break;\r
5068 \r
5069     case IDM_LoadProg2:\r
5070      LoadEnginePopUp(hwndMain, 1);\r
5071       break;\r
5072 \r
5073     case IDM_EditServers:\r
5074       EditTagsPopUp(icsNames, &icsNames);\r
5075       break;\r
5076 \r
5077     case IDM_EditTags:\r
5078     case IDM_Tags:\r
5079       EditTagsProc();\r
5080       break;\r
5081 \r
5082     case IDM_EditBook:\r
5083       EditBookEvent();\r
5084       break;\r
5085 \r
5086     case IDM_EditComment:\r
5087     case IDM_Comment:\r
5088       if (commentUp && editComment) {\r
5089         CommentPopDown();\r
5090       } else {\r
5091         EditCommentEvent();\r
5092       }\r
5093       break;\r
5094 \r
5095     case IDM_Pause:\r
5096       PauseEvent();\r
5097       break;\r
5098 \r
5099     case IDM_Accept:\r
5100       AcceptEvent();\r
5101       break;\r
5102 \r
5103     case IDM_Decline:\r
5104       DeclineEvent();\r
5105       break;\r
5106 \r
5107     case IDM_Rematch:\r
5108       RematchEvent();\r
5109       break;\r
5110 \r
5111     case IDM_CallFlag:\r
5112       CallFlagEvent();\r
5113       break;\r
5114 \r
5115     case IDM_Draw:\r
5116       DrawEvent();\r
5117       break;\r
5118 \r
5119     case IDM_Adjourn:\r
5120       AdjournEvent();\r
5121       break;\r
5122 \r
5123     case IDM_Abort:\r
5124       AbortEvent();\r
5125       break;\r
5126 \r
5127     case IDM_Resign:\r
5128       ResignEvent();\r
5129       break;\r
5130 \r
5131     case IDM_StopObserving:\r
5132       StopObservingEvent();\r
5133       break;\r
5134 \r
5135     case IDM_StopExamining:\r
5136       StopExaminingEvent();\r
5137       break;\r
5138 \r
5139     case IDM_Upload:\r
5140       UploadGameEvent();\r
5141       break;\r
5142 \r
5143     case IDM_TypeInMove:\r
5144       TypeInEvent('\000');\r
5145       break;\r
5146 \r
5147     case IDM_TypeInName:\r
5148       PopUpNameDialog('\000');\r
5149       break;\r
5150 \r
5151     case IDM_Backward:\r
5152       BackwardEvent();\r
5153       SetFocus(hwndMain);\r
5154       break;\r
5155 \r
5156     JAWS_MENU_ITEMS\r
5157 \r
5158     case IDM_Forward:\r
5159       ForwardEvent();\r
5160       SetFocus(hwndMain);\r
5161       break;\r
5162 \r
5163     case IDM_ToStart:\r
5164       ToStartEvent();\r
5165       SetFocus(hwndMain);\r
5166       break;\r
5167 \r
5168     case IDM_ToEnd:\r
5169       ToEndEvent();\r
5170       SetFocus(hwndMain);\r
5171       break;\r
5172 \r
5173     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5174     case OPT_GameListPrev:\r
5175       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5176       break;\r
5177 \r
5178     case IDM_Revert:\r
5179       RevertEvent(FALSE);\r
5180       break;\r
5181 \r
5182     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5183       RevertEvent(TRUE);\r
5184       break;\r
5185 \r
5186     case IDM_TruncateGame:\r
5187       TruncateGameEvent();\r
5188       break;\r
5189 \r
5190     case IDM_MoveNow:\r
5191       MoveNowEvent();\r
5192       break;\r
5193 \r
5194     case IDM_RetractMove:\r
5195       RetractMoveEvent();\r
5196       break;\r
5197 \r
5198     case IDM_FlipView:\r
5199       flipView = !flipView;\r
5200       DrawPosition(FALSE, NULL);\r
5201       break;\r
5202 \r
5203     case IDM_FlipClock:\r
5204       flipClock = !flipClock;\r
5205       DisplayBothClocks();\r
5206       DisplayLogos();\r
5207       break;\r
5208 \r
5209     case IDM_MuteSounds:\r
5210       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5211       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5212                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5213       break;\r
5214 \r
5215     case IDM_GeneralOptions:\r
5216       GeneralOptionsPopup(hwnd);\r
5217       DrawPosition(TRUE, NULL);\r
5218       break;\r
5219 \r
5220     case IDM_BoardOptions:\r
5221       BoardOptionsPopup(hwnd);\r
5222       break;\r
5223 \r
5224     case IDM_ThemeOptions:\r
5225       ThemeOptionsPopup(hwnd);\r
5226       break;\r
5227 \r
5228     case IDM_EnginePlayOptions:\r
5229       EnginePlayOptionsPopup(hwnd);\r
5230       break;\r
5231 \r
5232     case IDM_Engine1Options:\r
5233       EngineOptionsPopup(hwnd, &first);\r
5234       break;\r
5235 \r
5236     case IDM_Engine2Options:\r
5237       savedHwnd = hwnd;\r
5238       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5239       EngineOptionsPopup(hwnd, &second);\r
5240       break;\r
5241 \r
5242     case IDM_OptionsUCI:\r
5243       UciOptionsPopup(hwnd);\r
5244       break;\r
5245 \r
5246     case IDM_Tourney:\r
5247       TourneyPopup(hwnd);\r
5248       break;\r
5249 \r
5250     case IDM_IcsOptions:\r
5251       IcsOptionsPopup(hwnd);\r
5252       break;\r
5253 \r
5254     case IDM_Fonts:\r
5255       FontsOptionsPopup(hwnd);\r
5256       break;\r
5257 \r
5258     case IDM_Sounds:\r
5259       SoundOptionsPopup(hwnd);\r
5260       break;\r
5261 \r
5262     case IDM_CommPort:\r
5263       CommPortOptionsPopup(hwnd);\r
5264       break;\r
5265 \r
5266     case IDM_LoadOptions:\r
5267       LoadOptionsPopup(hwnd);\r
5268       break;\r
5269 \r
5270     case IDM_SaveOptions:\r
5271       SaveOptionsPopup(hwnd);\r
5272       break;\r
5273 \r
5274     case IDM_TimeControl:\r
5275       TimeControlOptionsPopup(hwnd);\r
5276       break;\r
5277 \r
5278     case IDM_SaveSettings:\r
5279       SaveSettings(settingsFileName);\r
5280       break;\r
5281 \r
5282     case IDM_SaveSettingsOnExit:\r
5283       saveSettingsOnExit = !saveSettingsOnExit;\r
5284       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5285                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5286                                          MF_CHECKED : MF_UNCHECKED));\r
5287       break;\r
5288 \r
5289     case IDM_Hint:\r
5290       HintEvent();\r
5291       break;\r
5292 \r
5293     case IDM_Book:\r
5294       BookEvent();\r
5295       break;\r
5296 \r
5297     case IDM_AboutGame:\r
5298       AboutGameEvent();\r
5299       break;\r
5300 \r
5301     case IDM_Debug:\r
5302       appData.debugMode = !appData.debugMode;\r
5303       if (appData.debugMode) {\r
5304         char dir[MSG_SIZ];\r
5305         GetCurrentDirectory(MSG_SIZ, dir);\r
5306         SetCurrentDirectory(installDir);\r
5307         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5308         SetCurrentDirectory(dir);\r
5309         setbuf(debugFP, NULL);\r
5310       } else {\r
5311         fclose(debugFP);\r
5312         debugFP = NULL;\r
5313       }\r
5314       break;\r
5315 \r
5316     case IDM_HELPCONTENTS:\r
5317       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5318           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5319           MessageBox (GetFocus(),\r
5320                     _("Unable to activate help"),\r
5321                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5322       }\r
5323       break;\r
5324 \r
5325     case IDM_HELPSEARCH:\r
5326         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5327             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5328         MessageBox (GetFocus(),\r
5329                     _("Unable to activate help"),\r
5330                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5331       }\r
5332       break;\r
5333 \r
5334     case IDM_HELPHELP:\r
5335       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5336         MessageBox (GetFocus(),\r
5337                     _("Unable to activate help"),\r
5338                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5339       }\r
5340       break;\r
5341 \r
5342     case IDM_ABOUT:\r
5343       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5344       DialogBox(hInst, \r
5345         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5346         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5347       FreeProcInstance(lpProc);\r
5348       break;\r
5349 \r
5350     case IDM_DirectCommand1:\r
5351       AskQuestionEvent(_("Direct Command"),\r
5352                        _("Send to chess program:"), "", "1");\r
5353       break;\r
5354     case IDM_DirectCommand2:\r
5355       AskQuestionEvent(_("Direct Command"),\r
5356                        _("Send to second chess program:"), "", "2");\r
5357       break;\r
5358 \r
5359     case EP_WhitePawn:\r
5360       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5361       fromX = fromY = -1;\r
5362       break;\r
5363 \r
5364     case EP_WhiteKnight:\r
5365       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5366       fromX = fromY = -1;\r
5367       break;\r
5368 \r
5369     case EP_WhiteBishop:\r
5370       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5371       fromX = fromY = -1;\r
5372       break;\r
5373 \r
5374     case EP_WhiteRook:\r
5375       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5376       fromX = fromY = -1;\r
5377       break;\r
5378 \r
5379     case EP_WhiteQueen:\r
5380       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5381       fromX = fromY = -1;\r
5382       break;\r
5383 \r
5384     case EP_WhiteFerz:\r
5385       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5386       fromX = fromY = -1;\r
5387       break;\r
5388 \r
5389     case EP_WhiteWazir:\r
5390       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5391       fromX = fromY = -1;\r
5392       break;\r
5393 \r
5394     case EP_WhiteAlfil:\r
5395       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5396       fromX = fromY = -1;\r
5397       break;\r
5398 \r
5399     case EP_WhiteCannon:\r
5400       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5401       fromX = fromY = -1;\r
5402       break;\r
5403 \r
5404     case EP_WhiteCardinal:\r
5405       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5406       fromX = fromY = -1;\r
5407       break;\r
5408 \r
5409     case EP_WhiteMarshall:\r
5410       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5411       fromX = fromY = -1;\r
5412       break;\r
5413 \r
5414     case EP_WhiteKing:\r
5415       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5416       fromX = fromY = -1;\r
5417       break;\r
5418 \r
5419     case EP_BlackPawn:\r
5420       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5421       fromX = fromY = -1;\r
5422       break;\r
5423 \r
5424     case EP_BlackKnight:\r
5425       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5426       fromX = fromY = -1;\r
5427       break;\r
5428 \r
5429     case EP_BlackBishop:\r
5430       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5431       fromX = fromY = -1;\r
5432       break;\r
5433 \r
5434     case EP_BlackRook:\r
5435       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5436       fromX = fromY = -1;\r
5437       break;\r
5438 \r
5439     case EP_BlackQueen:\r
5440       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5441       fromX = fromY = -1;\r
5442       break;\r
5443 \r
5444     case EP_BlackFerz:\r
5445       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5446       fromX = fromY = -1;\r
5447       break;\r
5448 \r
5449     case EP_BlackWazir:\r
5450       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5451       fromX = fromY = -1;\r
5452       break;\r
5453 \r
5454     case EP_BlackAlfil:\r
5455       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5456       fromX = fromY = -1;\r
5457       break;\r
5458 \r
5459     case EP_BlackCannon:\r
5460       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5461       fromX = fromY = -1;\r
5462       break;\r
5463 \r
5464     case EP_BlackCardinal:\r
5465       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5466       fromX = fromY = -1;\r
5467       break;\r
5468 \r
5469     case EP_BlackMarshall:\r
5470       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5471       fromX = fromY = -1;\r
5472       break;\r
5473 \r
5474     case EP_BlackKing:\r
5475       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5476       fromX = fromY = -1;\r
5477       break;\r
5478 \r
5479     case EP_EmptySquare:\r
5480       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5481       fromX = fromY = -1;\r
5482       break;\r
5483 \r
5484     case EP_ClearBoard:\r
5485       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5486       fromX = fromY = -1;\r
5487       break;\r
5488 \r
5489     case EP_White:\r
5490       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5491       fromX = fromY = -1;\r
5492       break;\r
5493 \r
5494     case EP_Black:\r
5495       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5496       fromX = fromY = -1;\r
5497       break;\r
5498 \r
5499     case EP_Promote:\r
5500       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5501       fromX = fromY = -1;\r
5502       break;\r
5503 \r
5504     case EP_Demote:\r
5505       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5506       fromX = fromY = -1;\r
5507       break;\r
5508 \r
5509     case DP_Pawn:\r
5510       DropMenuEvent(WhitePawn, fromX, fromY);\r
5511       fromX = fromY = -1;\r
5512       break;\r
5513 \r
5514     case DP_Knight:\r
5515       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5516       fromX = fromY = -1;\r
5517       break;\r
5518 \r
5519     case DP_Bishop:\r
5520       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5521       fromX = fromY = -1;\r
5522       break;\r
5523 \r
5524     case DP_Rook:\r
5525       DropMenuEvent(WhiteRook, fromX, fromY);\r
5526       fromX = fromY = -1;\r
5527       break;\r
5528 \r
5529     case DP_Queen:\r
5530       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5531       fromX = fromY = -1;\r
5532       break;\r
5533 \r
5534     case IDM_English:\r
5535       barbaric = 0; appData.language = "";\r
5536       TranslateMenus(0);\r
5537       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5538       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5539       lastChecked = wmId;\r
5540       break;\r
5541 \r
5542     default:\r
5543       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5544           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5545       else\r
5546       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5547           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5548           TranslateMenus(0);\r
5549           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5550           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5551           lastChecked = wmId;\r
5552           break;\r
5553       }\r
5554       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5555     }\r
5556     break;\r
5557 \r
5558   case WM_TIMER:\r
5559     switch (wParam) {\r
5560     case CLOCK_TIMER_ID:\r
5561       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5562       clockTimerEvent = 0;\r
5563       DecrementClocks(); /* call into back end */\r
5564       break;\r
5565     case LOAD_GAME_TIMER_ID:\r
5566       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5567       loadGameTimerEvent = 0;\r
5568       AutoPlayGameLoop(); /* call into back end */\r
5569       break;\r
5570     case ANALYSIS_TIMER_ID:\r
5571       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5572                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5573         AnalysisPeriodicEvent(0);\r
5574       } else {\r
5575         KillTimer(hwnd, analysisTimerEvent);\r
5576         analysisTimerEvent = 0;\r
5577       }\r
5578       break;\r
5579     case DELAYED_TIMER_ID:\r
5580       KillTimer(hwnd, delayedTimerEvent);\r
5581       delayedTimerEvent = 0;\r
5582       delayedTimerCallback();\r
5583       break;\r
5584     }\r
5585     break;\r
5586 \r
5587   case WM_USER_Input:\r
5588     InputEvent(hwnd, message, wParam, lParam);\r
5589     break;\r
5590 \r
5591   /* [AS] Also move "attached" child windows */\r
5592   case WM_WINDOWPOSCHANGING:\r
5593 \r
5594     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5595         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5596 \r
5597         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5598             /* Window is moving */\r
5599             RECT rcMain;\r
5600 \r
5601 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5602             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5603             rcMain.right  = wpMain.x + wpMain.width;\r
5604             rcMain.top    = wpMain.y;\r
5605             rcMain.bottom = wpMain.y + wpMain.height;\r
5606             \r
5607             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5608             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5609             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5610             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5611             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5612             wpMain.x = lpwp->x;\r
5613             wpMain.y = lpwp->y;\r
5614         }\r
5615     }\r
5616     break;\r
5617 \r
5618   /* [AS] Snapping */\r
5619   case WM_ENTERSIZEMOVE:\r
5620     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5621     if (hwnd == hwndMain) {\r
5622       doingSizing = TRUE;\r
5623       lastSizing = 0;\r
5624     }\r
5625     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5626     break;\r
5627 \r
5628   case WM_SIZING:\r
5629     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5630     if (hwnd == hwndMain) {\r
5631       lastSizing = wParam;\r
5632     }\r
5633     break;\r
5634 \r
5635   case WM_MOVING:\r
5636     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5637       return OnMoving( &sd, hwnd, wParam, lParam );\r
5638 \r
5639   case WM_EXITSIZEMOVE:\r
5640     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5641     if (hwnd == hwndMain) {\r
5642       RECT client;\r
5643       doingSizing = FALSE;\r
5644       InvalidateRect(hwnd, &boardRect, FALSE);\r
5645       GetClientRect(hwnd, &client);\r
5646       ResizeBoard(client.right, client.bottom, lastSizing);\r
5647       lastSizing = 0;\r
5648       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5649     }\r
5650     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5651     break;\r
5652 \r
5653   case WM_DESTROY: /* message: window being destroyed */\r
5654     PostQuitMessage(0);\r
5655     break;\r
5656 \r
5657   case WM_CLOSE:\r
5658     if (hwnd == hwndMain) {\r
5659       ExitEvent(0);\r
5660     }\r
5661     break;\r
5662 \r
5663   default:      /* Passes it on if unprocessed */\r
5664     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5665   }\r
5666   return 0;\r
5667 }\r
5668 \r
5669 /*---------------------------------------------------------------------------*\\r
5670  *\r
5671  * Misc utility routines\r
5672  *\r
5673 \*---------------------------------------------------------------------------*/\r
5674 \r
5675 /*\r
5676  * Decent random number generator, at least not as bad as Windows\r
5677  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5678  */\r
5679 unsigned int randstate;\r
5680 \r
5681 int\r
5682 myrandom(void)\r
5683 {\r
5684   randstate = randstate * 1664525 + 1013904223;\r
5685   return (int) randstate & 0x7fffffff;\r
5686 }\r
5687 \r
5688 void\r
5689 mysrandom(unsigned int seed)\r
5690 {\r
5691   randstate = seed;\r
5692 }\r
5693 \r
5694 \r
5695 /* \r
5696  * returns TRUE if user selects a different color, FALSE otherwise \r
5697  */\r
5698 \r
5699 BOOL\r
5700 ChangeColor(HWND hwnd, COLORREF *which)\r
5701 {\r
5702   static BOOL firstTime = TRUE;\r
5703   static DWORD customColors[16];\r
5704   CHOOSECOLOR cc;\r
5705   COLORREF newcolor;\r
5706   int i;\r
5707   ColorClass ccl;\r
5708 \r
5709   if (firstTime) {\r
5710     /* Make initial colors in use available as custom colors */\r
5711     /* Should we put the compiled-in defaults here instead? */\r
5712     i = 0;\r
5713     customColors[i++] = lightSquareColor & 0xffffff;\r
5714     customColors[i++] = darkSquareColor & 0xffffff;\r
5715     customColors[i++] = whitePieceColor & 0xffffff;\r
5716     customColors[i++] = blackPieceColor & 0xffffff;\r
5717     customColors[i++] = highlightSquareColor & 0xffffff;\r
5718     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5719 \r
5720     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5721       customColors[i++] = textAttribs[ccl].color;\r
5722     }\r
5723     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5724     firstTime = FALSE;\r
5725   }\r
5726 \r
5727   cc.lStructSize = sizeof(cc);\r
5728   cc.hwndOwner = hwnd;\r
5729   cc.hInstance = NULL;\r
5730   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5731   cc.lpCustColors = (LPDWORD) customColors;\r
5732   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5733 \r
5734   if (!ChooseColor(&cc)) return FALSE;\r
5735 \r
5736   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5737   if (newcolor == *which) return FALSE;\r
5738   *which = newcolor;\r
5739   return TRUE;\r
5740 \r
5741   /*\r
5742   InitDrawingColors();\r
5743   InvalidateRect(hwnd, &boardRect, FALSE);\r
5744   */\r
5745 }\r
5746 \r
5747 BOOLEAN\r
5748 MyLoadSound(MySound *ms)\r
5749 {\r
5750   BOOL ok = FALSE;\r
5751   struct stat st;\r
5752   FILE *f;\r
5753 \r
5754   if (ms->data && ms->flag) free(ms->data);\r
5755   ms->data = NULL;\r
5756 \r
5757   switch (ms->name[0]) {\r
5758   case NULLCHAR:\r
5759     /* Silence */\r
5760     ok = TRUE;\r
5761     break;\r
5762   case '$':\r
5763     /* System sound from Control Panel.  Don't preload here. */\r
5764     ok = TRUE;\r
5765     break;\r
5766   case '!':\r
5767     if (ms->name[1] == NULLCHAR) {\r
5768       /* "!" alone = silence */\r
5769       ok = TRUE;\r
5770     } else {\r
5771       /* Builtin wave resource.  Error if not found. */\r
5772       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5773       if (h == NULL) break;\r
5774       ms->data = (void *)LoadResource(hInst, h);\r
5775       ms->flag = 0; // not maloced, so cannot be freed!\r
5776       if (h == NULL) break;\r
5777       ok = TRUE;\r
5778     }\r
5779     break;\r
5780   default:\r
5781     /* .wav file.  Error if not found. */\r
5782     f = fopen(ms->name, "rb");\r
5783     if (f == NULL) break;\r
5784     if (fstat(fileno(f), &st) < 0) break;\r
5785     ms->data = malloc(st.st_size);\r
5786     ms->flag = 1;\r
5787     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5788     fclose(f);\r
5789     ok = TRUE;\r
5790     break;\r
5791   }\r
5792   if (!ok) {\r
5793     char buf[MSG_SIZ];\r
5794       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5795     DisplayError(buf, GetLastError());\r
5796   }\r
5797   return ok;\r
5798 }\r
5799 \r
5800 BOOLEAN\r
5801 MyPlaySound(MySound *ms)\r
5802 {\r
5803   BOOLEAN ok = FALSE;\r
5804 \r
5805   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5806   switch (ms->name[0]) {\r
5807   case NULLCHAR:\r
5808         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5809     /* Silence */\r
5810     ok = TRUE;\r
5811     break;\r
5812   case '$':\r
5813     /* System sound from Control Panel (deprecated feature).\r
5814        "$" alone or an unset sound name gets default beep (still in use). */\r
5815     if (ms->name[1]) {\r
5816       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5817     }\r
5818     if (!ok) ok = MessageBeep(MB_OK);\r
5819     break; \r
5820   case '!':\r
5821     /* Builtin wave resource, or "!" alone for silence */\r
5822     if (ms->name[1]) {\r
5823       if (ms->data == NULL) return FALSE;\r
5824       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5825     } else {\r
5826       ok = TRUE;\r
5827     }\r
5828     break;\r
5829   default:\r
5830     /* .wav file.  Error if not found. */\r
5831     if (ms->data == NULL) return FALSE;\r
5832     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5833     break;\r
5834   }\r
5835   /* Don't print an error: this can happen innocently if the sound driver\r
5836      is busy; for instance, if another instance of WinBoard is playing\r
5837      a sound at about the same time. */\r
5838   return ok;\r
5839 }\r
5840 \r
5841 \r
5842 LRESULT CALLBACK\r
5843 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5844 {\r
5845   BOOL ok;\r
5846   OPENFILENAME *ofn;\r
5847   static UINT *number; /* gross that this is static */\r
5848 \r
5849   switch (message) {\r
5850   case WM_INITDIALOG: /* message: initialize dialog box */\r
5851     /* Center the dialog over the application window */\r
5852     ofn = (OPENFILENAME *) lParam;\r
5853     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5854       number = (UINT *) ofn->lCustData;\r
5855       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5856     } else {\r
5857       number = NULL;\r
5858     }\r
5859     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5860     Translate(hDlg, 1536);\r
5861     return FALSE;  /* Allow for further processing */\r
5862 \r
5863   case WM_COMMAND:\r
5864     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5865       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5866     }\r
5867     return FALSE;  /* Allow for further processing */\r
5868   }\r
5869   return FALSE;\r
5870 }\r
5871 \r
5872 UINT APIENTRY\r
5873 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5874 {\r
5875   static UINT *number;\r
5876   OPENFILENAME *ofname;\r
5877   OFNOTIFY *ofnot;\r
5878   switch (uiMsg) {\r
5879   case WM_INITDIALOG:\r
5880     Translate(hdlg, DLG_IndexNumber);\r
5881     ofname = (OPENFILENAME *)lParam;\r
5882     number = (UINT *)(ofname->lCustData);\r
5883     break;\r
5884   case WM_NOTIFY:\r
5885     ofnot = (OFNOTIFY *)lParam;\r
5886     if (ofnot->hdr.code == CDN_FILEOK) {\r
5887       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5888     }\r
5889     break;\r
5890   }\r
5891   return 0;\r
5892 }\r
5893 \r
5894 \r
5895 FILE *\r
5896 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5897                char *nameFilt, char *dlgTitle, UINT *number,\r
5898                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5899 {\r
5900   OPENFILENAME openFileName;\r
5901   char buf1[MSG_SIZ];\r
5902   FILE *f;\r
5903 \r
5904   if (fileName == NULL) fileName = buf1;\r
5905   if (defName == NULL) {\r
5906     safeStrCpy(fileName, "*.", 3 );\r
5907     strcat(fileName, defExt);\r
5908   } else {\r
5909     safeStrCpy(fileName, defName, MSG_SIZ );\r
5910   }\r
5911     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5912   if (number) *number = 0;\r
5913 \r
5914   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5915   openFileName.hwndOwner         = hwnd;\r
5916   openFileName.hInstance         = (HANDLE) hInst;\r
5917   openFileName.lpstrFilter       = nameFilt;\r
5918   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5919   openFileName.nMaxCustFilter    = 0L;\r
5920   openFileName.nFilterIndex      = 1L;\r
5921   openFileName.lpstrFile         = fileName;\r
5922   openFileName.nMaxFile          = MSG_SIZ;\r
5923   openFileName.lpstrFileTitle    = fileTitle;\r
5924   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5925   openFileName.lpstrInitialDir   = NULL;\r
5926   openFileName.lpstrTitle        = dlgTitle;\r
5927   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5928     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5929     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5930     | (oldDialog ? 0 : OFN_EXPLORER);\r
5931   openFileName.nFileOffset       = 0;\r
5932   openFileName.nFileExtension    = 0;\r
5933   openFileName.lpstrDefExt       = defExt;\r
5934   openFileName.lCustData         = (LONG) number;\r
5935   openFileName.lpfnHook          = oldDialog ?\r
5936     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5937   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5938 \r
5939   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5940                         GetOpenFileName(&openFileName)) {\r
5941     /* open the file */\r
5942     f = fopen(openFileName.lpstrFile, write);\r
5943     if (f == NULL) {\r
5944       MessageBox(hwnd, _("File open failed"), NULL,\r
5945                  MB_OK|MB_ICONEXCLAMATION);\r
5946       return NULL;\r
5947     }\r
5948   } else {\r
5949     int err = CommDlgExtendedError();\r
5950     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5951     return FALSE;\r
5952   }\r
5953   return f;\r
5954 }\r
5955 \r
5956 \r
5957 \r
5958 VOID APIENTRY\r
5959 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5960 {\r
5961   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5962 \r
5963   /*\r
5964    * Get the first pop-up menu in the menu template. This is the\r
5965    * menu that TrackPopupMenu displays.\r
5966    */\r
5967   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5968   TranslateOneMenu(10, hmenuTrackPopup);\r
5969 \r
5970   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5971 \r
5972   /*\r
5973    * TrackPopup uses screen coordinates, so convert the\r
5974    * coordinates of the mouse click to screen coordinates.\r
5975    */\r
5976   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5977 \r
5978   /* Draw and track the floating pop-up menu. */\r
5979   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5980                  pt.x, pt.y, 0, hwnd, NULL);\r
5981 \r
5982   /* Destroy the menu.*/\r
5983   DestroyMenu(hmenu);\r
5984 }\r
5985    \r
5986 typedef struct {\r
5987   HWND hDlg, hText;\r
5988   int sizeX, sizeY, newSizeX, newSizeY;\r
5989   HDWP hdwp;\r
5990 } ResizeEditPlusButtonsClosure;\r
5991 \r
5992 BOOL CALLBACK\r
5993 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5994 {\r
5995   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5996   RECT rect;\r
5997   POINT pt;\r
5998 \r
5999   if (hChild == cl->hText) return TRUE;\r
6000   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6001   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6002   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6003   ScreenToClient(cl->hDlg, &pt);\r
6004   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6005     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6006   return TRUE;\r
6007 }\r
6008 \r
6009 /* Resize a dialog that has a (rich) edit field filling most of\r
6010    the top, with a row of buttons below */\r
6011 VOID\r
6012 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6013 {\r
6014   RECT rectText;\r
6015   int newTextHeight, newTextWidth;\r
6016   ResizeEditPlusButtonsClosure cl;\r
6017   \r
6018   /*if (IsIconic(hDlg)) return;*/\r
6019   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6020   \r
6021   cl.hdwp = BeginDeferWindowPos(8);\r
6022 \r
6023   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6024   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6025   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6026   if (newTextHeight < 0) {\r
6027     newSizeY += -newTextHeight;\r
6028     newTextHeight = 0;\r
6029   }\r
6030   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6031     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6032 \r
6033   cl.hDlg = hDlg;\r
6034   cl.hText = hText;\r
6035   cl.sizeX = sizeX;\r
6036   cl.sizeY = sizeY;\r
6037   cl.newSizeX = newSizeX;\r
6038   cl.newSizeY = newSizeY;\r
6039   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6040 \r
6041   EndDeferWindowPos(cl.hdwp);\r
6042 }\r
6043 \r
6044 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6045 {\r
6046     RECT    rChild, rParent;\r
6047     int     wChild, hChild, wParent, hParent;\r
6048     int     wScreen, hScreen, xNew, yNew;\r
6049     HDC     hdc;\r
6050 \r
6051     /* Get the Height and Width of the child window */\r
6052     GetWindowRect (hwndChild, &rChild);\r
6053     wChild = rChild.right - rChild.left;\r
6054     hChild = rChild.bottom - rChild.top;\r
6055 \r
6056     /* Get the Height and Width of the parent window */\r
6057     GetWindowRect (hwndParent, &rParent);\r
6058     wParent = rParent.right - rParent.left;\r
6059     hParent = rParent.bottom - rParent.top;\r
6060 \r
6061     /* Get the display limits */\r
6062     hdc = GetDC (hwndChild);\r
6063     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6064     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6065     ReleaseDC(hwndChild, hdc);\r
6066 \r
6067     /* Calculate new X position, then adjust for screen */\r
6068     xNew = rParent.left + ((wParent - wChild) /2);\r
6069     if (xNew < 0) {\r
6070         xNew = 0;\r
6071     } else if ((xNew+wChild) > wScreen) {\r
6072         xNew = wScreen - wChild;\r
6073     }\r
6074 \r
6075     /* Calculate new Y position, then adjust for screen */\r
6076     if( mode == 0 ) {\r
6077         yNew = rParent.top  + ((hParent - hChild) /2);\r
6078     }\r
6079     else {\r
6080         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6081     }\r
6082 \r
6083     if (yNew < 0) {\r
6084         yNew = 0;\r
6085     } else if ((yNew+hChild) > hScreen) {\r
6086         yNew = hScreen - hChild;\r
6087     }\r
6088 \r
6089     /* Set it, and return */\r
6090     return SetWindowPos (hwndChild, NULL,\r
6091                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6092 }\r
6093 \r
6094 /* Center one window over another */\r
6095 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6096 {\r
6097     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6098 }\r
6099 \r
6100 /*---------------------------------------------------------------------------*\\r
6101  *\r
6102  * Startup Dialog functions\r
6103  *\r
6104 \*---------------------------------------------------------------------------*/\r
6105 void\r
6106 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6107 {\r
6108   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6109 \r
6110   while (*cd != NULL) {\r
6111     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6112     cd++;\r
6113   }\r
6114 }\r
6115 \r
6116 void\r
6117 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6118 {\r
6119   char buf1[MAX_ARG_LEN];\r
6120   int len;\r
6121 \r
6122   if (str[0] == '@') {\r
6123     FILE* f = fopen(str + 1, "r");\r
6124     if (f == NULL) {\r
6125       DisplayFatalError(str + 1, errno, 2);\r
6126       return;\r
6127     }\r
6128     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6129     fclose(f);\r
6130     buf1[len] = NULLCHAR;\r
6131     str = buf1;\r
6132   }\r
6133 \r
6134   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6135 \r
6136   for (;;) {\r
6137     char buf[MSG_SIZ];\r
6138     char *end = strchr(str, '\n');\r
6139     if (end == NULL) return;\r
6140     memcpy(buf, str, end - str);\r
6141     buf[end - str] = NULLCHAR;\r
6142     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6143     str = end + 1;\r
6144   }\r
6145 }\r
6146 \r
6147 void\r
6148 SetStartupDialogEnables(HWND hDlg)\r
6149 {\r
6150   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6151     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6152     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6153   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6154     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6155   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6156     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6157   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6158     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6159   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6160     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6161     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6162     IsDlgButtonChecked(hDlg, OPT_View));\r
6163 }\r
6164 \r
6165 char *\r
6166 QuoteForFilename(char *filename)\r
6167 {\r
6168   int dquote, space;\r
6169   dquote = strchr(filename, '"') != NULL;\r
6170   space = strchr(filename, ' ') != NULL;\r
6171   if (dquote || space) {\r
6172     if (dquote) {\r
6173       return "'";\r
6174     } else {\r
6175       return "\"";\r
6176     }\r
6177   } else {\r
6178     return "";\r
6179   }\r
6180 }\r
6181 \r
6182 VOID\r
6183 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6184 {\r
6185   char buf[MSG_SIZ];\r
6186   char *q;\r
6187 \r
6188   InitComboStringsFromOption(hwndCombo, nthnames);\r
6189   q = QuoteForFilename(nthcp);\r
6190     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6191   if (*nthdir != NULLCHAR) {\r
6192     q = QuoteForFilename(nthdir);\r
6193       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6194   }\r
6195   if (*nthcp == NULLCHAR) {\r
6196     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6197   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6198     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6199     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6200   }\r
6201 }\r
6202 \r
6203 LRESULT CALLBACK\r
6204 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6205 {\r
6206   char buf[MSG_SIZ];\r
6207   HANDLE hwndCombo;\r
6208   char *p;\r
6209 \r
6210   switch (message) {\r
6211   case WM_INITDIALOG:\r
6212     /* Center the dialog */\r
6213     CenterWindow (hDlg, GetDesktopWindow());\r
6214     Translate(hDlg, DLG_Startup);\r
6215     /* Initialize the dialog items */\r
6216     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6217                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6218                   firstChessProgramNames);\r
6219     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6220                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6221                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6222     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6223     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6224       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6225     if (*appData.icsHelper != NULLCHAR) {\r
6226       char *q = QuoteForFilename(appData.icsHelper);\r
6227       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6228     }\r
6229     if (*appData.icsHost == NULLCHAR) {\r
6230       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6231       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6232     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6233       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6234       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6235     }\r
6236 \r
6237     if (appData.icsActive) {\r
6238       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6239     }\r
6240     else if (appData.noChessProgram) {\r
6241       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6242     }\r
6243     else {\r
6244       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6245     }\r
6246 \r
6247     SetStartupDialogEnables(hDlg);\r
6248     return TRUE;\r
6249 \r
6250   case WM_COMMAND:\r
6251     switch (LOWORD(wParam)) {\r
6252     case IDOK:\r
6253       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6254         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6255         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6256         p = buf;\r
6257         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6258         ParseArgs(StringGet, &p);\r
6259         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6260         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6261         p = buf;\r
6262         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6263         ParseArgs(StringGet, &p);\r
6264         SwapEngines(singleList); // ... and then make it 'second'\r
6265         appData.noChessProgram = FALSE;\r
6266         appData.icsActive = FALSE;\r
6267       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6268         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6269         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6270         p = buf;\r
6271         ParseArgs(StringGet, &p);\r
6272         if (appData.zippyPlay) {\r
6273           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6274           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6275           p = buf;\r
6276           ParseArgs(StringGet, &p);\r
6277         }\r
6278       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6279         appData.noChessProgram = TRUE;\r
6280         appData.icsActive = FALSE;\r
6281       } else {\r
6282         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6283                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6284         return TRUE;\r
6285       }\r
6286       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6287         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6288         p = buf;\r
6289         ParseArgs(StringGet, &p);\r
6290       }\r
6291       EndDialog(hDlg, TRUE);\r
6292       return TRUE;\r
6293 \r
6294     case IDCANCEL:\r
6295       ExitEvent(0);\r
6296       return TRUE;\r
6297 \r
6298     case IDM_HELPCONTENTS:\r
6299       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6300         MessageBox (GetFocus(),\r
6301                     _("Unable to activate help"),\r
6302                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6303       }\r
6304       break;\r
6305 \r
6306     default:\r
6307       SetStartupDialogEnables(hDlg);\r
6308       break;\r
6309     }\r
6310     break;\r
6311   }\r
6312   return FALSE;\r
6313 }\r
6314 \r
6315 /*---------------------------------------------------------------------------*\\r
6316  *\r
6317  * About box dialog functions\r
6318  *\r
6319 \*---------------------------------------------------------------------------*/\r
6320 \r
6321 /* Process messages for "About" dialog box */\r
6322 LRESULT CALLBACK\r
6323 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6324 {\r
6325   switch (message) {\r
6326   case WM_INITDIALOG: /* message: initialize dialog box */\r
6327     /* Center the dialog over the application window */\r
6328     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6329     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6330     Translate(hDlg, ABOUTBOX);\r
6331     JAWS_COPYRIGHT\r
6332     return (TRUE);\r
6333 \r
6334   case WM_COMMAND: /* message: received a command */\r
6335     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6336         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6337       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6338       return (TRUE);\r
6339     }\r
6340     break;\r
6341   }\r
6342   return (FALSE);\r
6343 }\r
6344 \r
6345 /*---------------------------------------------------------------------------*\\r
6346  *\r
6347  * Comment Dialog functions\r
6348  *\r
6349 \*---------------------------------------------------------------------------*/\r
6350 \r
6351 LRESULT CALLBACK\r
6352 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6353 {\r
6354   static HANDLE hwndText = NULL;\r
6355   int len, newSizeX, newSizeY, flags;\r
6356   static int sizeX, sizeY;\r
6357   char *str;\r
6358   RECT rect;\r
6359   MINMAXINFO *mmi;\r
6360 \r
6361   switch (message) {\r
6362   case WM_INITDIALOG: /* message: initialize dialog box */\r
6363     /* Initialize the dialog items */\r
6364     Translate(hDlg, DLG_EditComment);\r
6365     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6366     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6367     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6368     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6369     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6370     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6371     SetWindowText(hDlg, commentTitle);\r
6372     if (editComment) {\r
6373       SetFocus(hwndText);\r
6374     } else {\r
6375       SetFocus(GetDlgItem(hDlg, IDOK));\r
6376     }\r
6377     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6378                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6379                 MAKELPARAM(FALSE, 0));\r
6380     /* Size and position the dialog */\r
6381     if (!commentDialog) {\r
6382       commentDialog = hDlg;\r
6383       flags = SWP_NOZORDER;\r
6384       GetClientRect(hDlg, &rect);\r
6385       sizeX = rect.right;\r
6386       sizeY = rect.bottom;\r
6387       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6388           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6389         WINDOWPLACEMENT wp;\r
6390         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6391         wp.length = sizeof(WINDOWPLACEMENT);\r
6392         wp.flags = 0;\r
6393         wp.showCmd = SW_SHOW;\r
6394         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6395         wp.rcNormalPosition.left = wpComment.x;\r
6396         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6397         wp.rcNormalPosition.top = wpComment.y;\r
6398         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6399         SetWindowPlacement(hDlg, &wp);\r
6400 \r
6401         GetClientRect(hDlg, &rect);\r
6402         newSizeX = rect.right;\r
6403         newSizeY = rect.bottom;\r
6404         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6405                               newSizeX, newSizeY);\r
6406         sizeX = newSizeX;\r
6407         sizeY = newSizeY;\r
6408       }\r
6409     }\r
6410     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6411     return FALSE;\r
6412 \r
6413   case WM_COMMAND: /* message: received a command */\r
6414     switch (LOWORD(wParam)) {\r
6415     case IDOK:\r
6416       if (editComment) {\r
6417         char *p, *q;\r
6418         /* Read changed options from the dialog box */\r
6419         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6420         len = GetWindowTextLength(hwndText);\r
6421         str = (char *) malloc(len + 1);\r
6422         GetWindowText(hwndText, str, len + 1);\r
6423         p = q = str;\r
6424         while (*q) {\r
6425           if (*q == '\r')\r
6426             q++;\r
6427           else\r
6428             *p++ = *q++;\r
6429         }\r
6430         *p = NULLCHAR;\r
6431         ReplaceComment(commentIndex, str);\r
6432         free(str);\r
6433       }\r
6434       CommentPopDown();\r
6435       return TRUE;\r
6436 \r
6437     case IDCANCEL:\r
6438     case OPT_CancelComment:\r
6439       CommentPopDown();\r
6440       return TRUE;\r
6441 \r
6442     case OPT_ClearComment:\r
6443       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6444       break;\r
6445 \r
6446     case OPT_EditComment:\r
6447       EditCommentEvent();\r
6448       return TRUE;\r
6449 \r
6450     default:\r
6451       break;\r
6452     }\r
6453     break;\r
6454 \r
6455   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6456         if( wParam == OPT_CommentText ) {\r
6457             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6458 \r
6459             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6460                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6461                 POINTL pt;\r
6462                 LRESULT index;\r
6463 \r
6464                 pt.x = LOWORD( lpMF->lParam );\r
6465                 pt.y = HIWORD( lpMF->lParam );\r
6466 \r
6467                 if(lpMF->msg == WM_CHAR) {\r
6468                         CHARRANGE sel;\r
6469                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6470                         index = sel.cpMin;\r
6471                 } else\r
6472                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6473 \r
6474                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6475                 len = GetWindowTextLength(hwndText);\r
6476                 str = (char *) malloc(len + 1);\r
6477                 GetWindowText(hwndText, str, len + 1);\r
6478                 ReplaceComment(commentIndex, str);\r
6479                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6480                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6481                 free(str);\r
6482 \r
6483                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6484                 lpMF->msg = WM_USER;\r
6485 \r
6486                 return TRUE;\r
6487             }\r
6488         }\r
6489         break;\r
6490 \r
6491   case WM_SIZE:\r
6492     newSizeX = LOWORD(lParam);\r
6493     newSizeY = HIWORD(lParam);\r
6494     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6495     sizeX = newSizeX;\r
6496     sizeY = newSizeY;\r
6497     break;\r
6498 \r
6499   case WM_GETMINMAXINFO:\r
6500     /* Prevent resizing window too small */\r
6501     mmi = (MINMAXINFO *) lParam;\r
6502     mmi->ptMinTrackSize.x = 100;\r
6503     mmi->ptMinTrackSize.y = 100;\r
6504     break;\r
6505   }\r
6506   return FALSE;\r
6507 }\r
6508 \r
6509 VOID\r
6510 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6511 {\r
6512   FARPROC lpProc;\r
6513   char *p, *q;\r
6514 \r
6515   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6516 \r
6517   if (str == NULL) str = "";\r
6518   p = (char *) malloc(2 * strlen(str) + 2);\r
6519   q = p;\r
6520   while (*str) {\r
6521     if (*str == '\n') *q++ = '\r';\r
6522     *q++ = *str++;\r
6523   }\r
6524   *q = NULLCHAR;\r
6525   if (commentText != NULL) free(commentText);\r
6526 \r
6527   commentIndex = index;\r
6528   commentTitle = title;\r
6529   commentText = p;\r
6530   editComment = edit;\r
6531 \r
6532   if (commentDialog) {\r
6533     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6534     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6535   } else {\r
6536     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6537     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6538                  hwndMain, (DLGPROC)lpProc);\r
6539     FreeProcInstance(lpProc);\r
6540   }\r
6541   commentUp = TRUE;\r
6542 }\r
6543 \r
6544 \r
6545 /*---------------------------------------------------------------------------*\\r
6546  *\r
6547  * Type-in move dialog functions\r
6548  * \r
6549 \*---------------------------------------------------------------------------*/\r
6550 \r
6551 LRESULT CALLBACK\r
6552 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6553 {\r
6554   char move[MSG_SIZ];\r
6555   HWND hInput;\r
6556 \r
6557   switch (message) {\r
6558   case WM_INITDIALOG:\r
6559     move[0] = (char) lParam;\r
6560     move[1] = NULLCHAR;\r
6561     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6562     Translate(hDlg, DLG_TypeInMove);\r
6563     hInput = GetDlgItem(hDlg, OPT_Move);\r
6564     SetWindowText(hInput, move);\r
6565     SetFocus(hInput);\r
6566     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6567     return FALSE;\r
6568 \r
6569   case WM_COMMAND:\r
6570     switch (LOWORD(wParam)) {\r
6571     case IDOK:\r
6572 \r
6573       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6574       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6575       TypeInDoneEvent(move);\r
6576       EndDialog(hDlg, TRUE);\r
6577       return TRUE;\r
6578     case IDCANCEL:\r
6579       EndDialog(hDlg, FALSE);\r
6580       return TRUE;\r
6581     default:\r
6582       break;\r
6583     }\r
6584     break;\r
6585   }\r
6586   return FALSE;\r
6587 }\r
6588 \r
6589 VOID\r
6590 PopUpMoveDialog(char firstchar)\r
6591 {\r
6592     FARPROC lpProc;\r
6593 \r
6594       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6595       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6596         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6597       FreeProcInstance(lpProc);\r
6598 }\r
6599 \r
6600 /*---------------------------------------------------------------------------*\\r
6601  *\r
6602  * Type-in name dialog functions\r
6603  * \r
6604 \*---------------------------------------------------------------------------*/\r
6605 \r
6606 LRESULT CALLBACK\r
6607 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6608 {\r
6609   char move[MSG_SIZ];\r
6610   HWND hInput;\r
6611 \r
6612   switch (message) {\r
6613   case WM_INITDIALOG:\r
6614     move[0] = (char) lParam;\r
6615     move[1] = NULLCHAR;\r
6616     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6617     Translate(hDlg, DLG_TypeInName);\r
6618     hInput = GetDlgItem(hDlg, OPT_Name);\r
6619     SetWindowText(hInput, move);\r
6620     SetFocus(hInput);\r
6621     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6622     return FALSE;\r
6623 \r
6624   case WM_COMMAND:\r
6625     switch (LOWORD(wParam)) {\r
6626     case IDOK:\r
6627       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6628       appData.userName = strdup(move);\r
6629       SetUserLogo();\r
6630       SetGameInfo();\r
6631       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6632         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6633         DisplayTitle(move);\r
6634       }\r
6635 \r
6636 \r
6637       EndDialog(hDlg, TRUE);\r
6638       return TRUE;\r
6639     case IDCANCEL:\r
6640       EndDialog(hDlg, FALSE);\r
6641       return TRUE;\r
6642     default:\r
6643       break;\r
6644     }\r
6645     break;\r
6646   }\r
6647   return FALSE;\r
6648 }\r
6649 \r
6650 VOID\r
6651 PopUpNameDialog(char firstchar)\r
6652 {\r
6653     FARPROC lpProc;\r
6654     \r
6655       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6656       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6657         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6658       FreeProcInstance(lpProc);\r
6659 }\r
6660 \r
6661 /*---------------------------------------------------------------------------*\\r
6662  *\r
6663  *  Error dialogs\r
6664  * \r
6665 \*---------------------------------------------------------------------------*/\r
6666 \r
6667 /* Nonmodal error box */\r
6668 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6669                              WPARAM wParam, LPARAM lParam);\r
6670 \r
6671 VOID\r
6672 ErrorPopUp(char *title, char *content)\r
6673 {\r
6674   FARPROC lpProc;\r
6675   char *p, *q;\r
6676   BOOLEAN modal = hwndMain == NULL;\r
6677 \r
6678   p = content;\r
6679   q = errorMessage;\r
6680   while (*p) {\r
6681     if (*p == '\n') {\r
6682       if (modal) {\r
6683         *q++ = ' ';\r
6684         p++;\r
6685       } else {\r
6686         *q++ = '\r';\r
6687         *q++ = *p++;\r
6688       }\r
6689     } else {\r
6690       *q++ = *p++;\r
6691     }\r
6692   }\r
6693   *q = NULLCHAR;\r
6694   strncpy(errorTitle, title, sizeof(errorTitle));\r
6695   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6696   \r
6697   if (modal) {\r
6698     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6699   } else {\r
6700     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6701     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6702                  hwndMain, (DLGPROC)lpProc);\r
6703     FreeProcInstance(lpProc);\r
6704   }\r
6705 }\r
6706 \r
6707 VOID\r
6708 ErrorPopDown()\r
6709 {\r
6710   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6711   if (errorDialog == NULL) return;\r
6712   DestroyWindow(errorDialog);\r
6713   errorDialog = NULL;\r
6714   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6715 }\r
6716 \r
6717 LRESULT CALLBACK\r
6718 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6719 {\r
6720   HANDLE hwndText;\r
6721   RECT rChild;\r
6722 \r
6723   switch (message) {\r
6724   case WM_INITDIALOG:\r
6725     GetWindowRect(hDlg, &rChild);\r
6726 \r
6727     /*\r
6728     SetWindowPos(hDlg, NULL, rChild.left,\r
6729       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6730       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6731     */\r
6732 \r
6733     /* \r
6734         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6735         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6736         and it doesn't work when you resize the dialog.\r
6737         For now, just give it a default position.\r
6738     */\r
6739     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6740     Translate(hDlg, DLG_Error);\r
6741 \r
6742     errorDialog = hDlg;\r
6743     SetWindowText(hDlg, errorTitle);\r
6744     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6745     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6746     return FALSE;\r
6747 \r
6748   case WM_COMMAND:\r
6749     switch (LOWORD(wParam)) {\r
6750     case IDOK:\r
6751     case IDCANCEL:\r
6752       if (errorDialog == hDlg) errorDialog = NULL;\r
6753       DestroyWindow(hDlg);\r
6754       return TRUE;\r
6755 \r
6756     default:\r
6757       break;\r
6758     }\r
6759     break;\r
6760   }\r
6761   return FALSE;\r
6762 }\r
6763 \r
6764 #ifdef GOTHIC\r
6765 HWND gothicDialog = NULL;\r
6766 \r
6767 LRESULT CALLBACK\r
6768 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6769 {\r
6770   HANDLE hwndText;\r
6771   RECT rChild;\r
6772   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6773 \r
6774   switch (message) {\r
6775   case WM_INITDIALOG:\r
6776     GetWindowRect(hDlg, &rChild);\r
6777 \r
6778     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6779                                                              SWP_NOZORDER);\r
6780 \r
6781     /* \r
6782         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6783         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6784         and it doesn't work when you resize the dialog.\r
6785         For now, just give it a default position.\r
6786     */\r
6787     gothicDialog = hDlg;\r
6788     SetWindowText(hDlg, errorTitle);\r
6789     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6790     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6791     return FALSE;\r
6792 \r
6793   case WM_COMMAND:\r
6794     switch (LOWORD(wParam)) {\r
6795     case IDOK:\r
6796     case IDCANCEL:\r
6797       if (errorDialog == hDlg) errorDialog = NULL;\r
6798       DestroyWindow(hDlg);\r
6799       return TRUE;\r
6800 \r
6801     default:\r
6802       break;\r
6803     }\r
6804     break;\r
6805   }\r
6806   return FALSE;\r
6807 }\r
6808 \r
6809 VOID\r
6810 GothicPopUp(char *title, VariantClass variant)\r
6811 {\r
6812   FARPROC lpProc;\r
6813   static char *lastTitle;\r
6814 \r
6815   strncpy(errorTitle, title, sizeof(errorTitle));\r
6816   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6817 \r
6818   if(lastTitle != title && gothicDialog != NULL) {\r
6819     DestroyWindow(gothicDialog);\r
6820     gothicDialog = NULL;\r
6821   }\r
6822   if(variant != VariantNormal && gothicDialog == NULL) {\r
6823     title = lastTitle;\r
6824     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6825     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6826                  hwndMain, (DLGPROC)lpProc);\r
6827     FreeProcInstance(lpProc);\r
6828   }\r
6829 }\r
6830 #endif\r
6831 \r
6832 /*---------------------------------------------------------------------------*\\r
6833  *\r
6834  *  Ics Interaction console functions\r
6835  *\r
6836 \*---------------------------------------------------------------------------*/\r
6837 \r
6838 #define HISTORY_SIZE 64\r
6839 static char *history[HISTORY_SIZE];\r
6840 int histIn = 0, histP = 0;\r
6841 \r
6842 VOID\r
6843 SaveInHistory(char *cmd)\r
6844 {\r
6845   if (history[histIn] != NULL) {\r
6846     free(history[histIn]);\r
6847     history[histIn] = NULL;\r
6848   }\r
6849   if (*cmd == NULLCHAR) return;\r
6850   history[histIn] = StrSave(cmd);\r
6851   histIn = (histIn + 1) % HISTORY_SIZE;\r
6852   if (history[histIn] != NULL) {\r
6853     free(history[histIn]);\r
6854     history[histIn] = NULL;\r
6855   }\r
6856   histP = histIn;\r
6857 }\r
6858 \r
6859 char *\r
6860 PrevInHistory(char *cmd)\r
6861 {\r
6862   int newhp;\r
6863   if (histP == histIn) {\r
6864     if (history[histIn] != NULL) free(history[histIn]);\r
6865     history[histIn] = StrSave(cmd);\r
6866   }\r
6867   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6868   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6869   histP = newhp;\r
6870   return history[histP];\r
6871 }\r
6872 \r
6873 char *\r
6874 NextInHistory()\r
6875 {\r
6876   if (histP == histIn) return NULL;\r
6877   histP = (histP + 1) % HISTORY_SIZE;\r
6878   return history[histP];   \r
6879 }\r
6880 \r
6881 HMENU\r
6882 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6883 {\r
6884   HMENU hmenu, h;\r
6885   int i = 0;\r
6886   hmenu = LoadMenu(hInst, "TextMenu");\r
6887   h = GetSubMenu(hmenu, 0);\r
6888   while (e->item) {\r
6889     if (strcmp(e->item, "-") == 0) {\r
6890       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6891     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6892       int flags = MF_STRING, j = 0;\r
6893       if (e->item[0] == '|') {\r
6894         flags |= MF_MENUBARBREAK;\r
6895         j++;\r
6896       }\r
6897       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6898       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6899     }\r
6900     e++;\r
6901     i++;\r
6902   } \r
6903   return hmenu;\r
6904 }\r
6905 \r
6906 WNDPROC consoleTextWindowProc;\r
6907 \r
6908 void\r
6909 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6910 {\r
6911   char buf[MSG_SIZ], name[MSG_SIZ];\r
6912   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6913   CHARRANGE sel;\r
6914 \r
6915   if (!getname) {\r
6916     SetWindowText(hInput, command);\r
6917     if (immediate) {\r
6918       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6919     } else {\r
6920       sel.cpMin = 999999;\r
6921       sel.cpMax = 999999;\r
6922       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6923       SetFocus(hInput);\r
6924     }\r
6925     return;\r
6926   }    \r
6927   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6928   if (sel.cpMin == sel.cpMax) {\r
6929     /* Expand to surrounding word */\r
6930     TEXTRANGE tr;\r
6931     do {\r
6932       tr.chrg.cpMax = sel.cpMin;\r
6933       tr.chrg.cpMin = --sel.cpMin;\r
6934       if (sel.cpMin < 0) break;\r
6935       tr.lpstrText = name;\r
6936       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6937     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6938     sel.cpMin++;\r
6939 \r
6940     do {\r
6941       tr.chrg.cpMin = sel.cpMax;\r
6942       tr.chrg.cpMax = ++sel.cpMax;\r
6943       tr.lpstrText = name;\r
6944       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6945     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6946     sel.cpMax--;\r
6947 \r
6948     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6949       MessageBeep(MB_ICONEXCLAMATION);\r
6950       return;\r
6951     }\r
6952     tr.chrg = sel;\r
6953     tr.lpstrText = name;\r
6954     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6955   } else {\r
6956     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6957       MessageBeep(MB_ICONEXCLAMATION);\r
6958       return;\r
6959     }\r
6960     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6961   }\r
6962   if (immediate) {\r
6963     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6964     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6965     SetWindowText(hInput, buf);\r
6966     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6967   } else {\r
6968     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6969       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6970     SetWindowText(hInput, buf);\r
6971     sel.cpMin = 999999;\r
6972     sel.cpMax = 999999;\r
6973     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6974     SetFocus(hInput);\r
6975   }\r
6976 }\r
6977 \r
6978 LRESULT CALLBACK \r
6979 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6980 {\r
6981   HWND hInput;\r
6982   CHARRANGE sel;\r
6983 \r
6984   switch (message) {\r
6985   case WM_KEYDOWN:\r
6986     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6987     if(wParam=='R') return 0;\r
6988     switch (wParam) {\r
6989     case VK_PRIOR:\r
6990       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6991       return 0;\r
6992     case VK_NEXT:\r
6993       sel.cpMin = 999999;\r
6994       sel.cpMax = 999999;\r
6995       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6996       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6997       return 0;\r
6998     }\r
6999     break;\r
7000   case WM_CHAR:\r
7001    if(wParam != '\022') {\r
7002     if (wParam == '\t') {\r
7003       if (GetKeyState(VK_SHIFT) < 0) {\r
7004         /* shifted */\r
7005         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7006         if (buttonDesc[0].hwnd) {\r
7007           SetFocus(buttonDesc[0].hwnd);\r
7008         } else {\r
7009           SetFocus(hwndMain);\r
7010         }\r
7011       } else {\r
7012         /* unshifted */\r
7013         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7014       }\r
7015     } else {\r
7016       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7017       JAWS_DELETE( SetFocus(hInput); )\r
7018       SendMessage(hInput, message, wParam, lParam);\r
7019     }\r
7020     return 0;\r
7021    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
7022    lParam = -1;\r
7023   case WM_RBUTTONDOWN:\r
7024     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7025       /* Move selection here if it was empty */\r
7026       POINT pt;\r
7027       pt.x = LOWORD(lParam);\r
7028       pt.y = HIWORD(lParam);\r
7029       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7030       if (sel.cpMin == sel.cpMax) {\r
7031         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7032         sel.cpMax = sel.cpMin;\r
7033         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7034       }\r
7035       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7036 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
7037       POINT pt;\r
7038       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7039       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7040       if (sel.cpMin == sel.cpMax) {\r
7041         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7042         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7043       }\r
7044       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7045         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7046       }\r
7047       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
7048       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
7049       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
7050       MenuPopup(hwnd, pt, hmenu, -1);\r
7051 }\r
7052     }\r
7053     return 0;\r
7054   case WM_RBUTTONUP:\r
7055     if (GetKeyState(VK_SHIFT) & ~1) {\r
7056       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7057         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7058     }\r
7059     return 0;\r
7060   case WM_PASTE:\r
7061     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7062     SetFocus(hInput);\r
7063     return SendMessage(hInput, message, wParam, lParam);\r
7064   case WM_MBUTTONDOWN:\r
7065     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7066   case WM_COMMAND:\r
7067     switch (LOWORD(wParam)) {\r
7068     case IDM_QuickPaste:\r
7069       {\r
7070         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7071         if (sel.cpMin == sel.cpMax) {\r
7072           MessageBeep(MB_ICONEXCLAMATION);\r
7073           return 0;\r
7074         }\r
7075         SendMessage(hwnd, WM_COPY, 0, 0);\r
7076         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7077         SendMessage(hInput, WM_PASTE, 0, 0);\r
7078         SetFocus(hInput);\r
7079         return 0;\r
7080       }\r
7081     case IDM_Cut:\r
7082       SendMessage(hwnd, WM_CUT, 0, 0);\r
7083       return 0;\r
7084     case IDM_Paste:\r
7085       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7086       return 0;\r
7087     case IDM_Copy:\r
7088       SendMessage(hwnd, WM_COPY, 0, 0);\r
7089       return 0;\r
7090     default:\r
7091       {\r
7092         int i = LOWORD(wParam) - IDM_CommandX;\r
7093         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7094             icsTextMenuEntry[i].command != NULL) {\r
7095           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7096                    icsTextMenuEntry[i].getname,\r
7097                    icsTextMenuEntry[i].immediate);\r
7098           return 0;\r
7099         }\r
7100       }\r
7101       break;\r
7102     }\r
7103     break;\r
7104   }\r
7105   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7106 }\r
7107 \r
7108 WNDPROC consoleInputWindowProc;\r
7109 \r
7110 LRESULT CALLBACK\r
7111 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7112 {\r
7113   char buf[MSG_SIZ];\r
7114   char *p;\r
7115   static BOOL sendNextChar = FALSE;\r
7116   static BOOL quoteNextChar = FALSE;\r
7117   InputSource *is = consoleInputSource;\r
7118   CHARFORMAT cf;\r
7119   CHARRANGE sel;\r
7120 \r
7121   switch (message) {\r
7122   case WM_CHAR:\r
7123     if (!appData.localLineEditing || sendNextChar) {\r
7124       is->buf[0] = (CHAR) wParam;\r
7125       is->count = 1;\r
7126       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7127       sendNextChar = FALSE;\r
7128       return 0;\r
7129     }\r
7130     if (quoteNextChar) {\r
7131       buf[0] = (char) wParam;\r
7132       buf[1] = NULLCHAR;\r
7133       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7134       quoteNextChar = FALSE;\r
7135       return 0;\r
7136     }\r
7137     switch (wParam) {\r
7138     case '\r':   /* Enter key */\r
7139       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7140       if (consoleEcho) SaveInHistory(is->buf);\r
7141       is->buf[is->count++] = '\n';\r
7142       is->buf[is->count] = NULLCHAR;\r
7143       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7144       if (consoleEcho) {\r
7145         ConsoleOutput(is->buf, is->count, TRUE);\r
7146       } else if (appData.localLineEditing) {\r
7147         ConsoleOutput("\n", 1, TRUE);\r
7148       }\r
7149       /* fall thru */\r
7150     case '\033': /* Escape key */\r
7151       SetWindowText(hwnd, "");\r
7152       cf.cbSize = sizeof(CHARFORMAT);\r
7153       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7154       if (consoleEcho) {\r
7155         cf.crTextColor = textAttribs[ColorNormal].color;\r
7156       } else {\r
7157         cf.crTextColor = COLOR_ECHOOFF;\r
7158       }\r
7159       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7160       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7161       return 0;\r
7162     case '\t':   /* Tab key */\r
7163       if (GetKeyState(VK_SHIFT) < 0) {\r
7164         /* shifted */\r
7165         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7166       } else {\r
7167         /* unshifted */\r
7168         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7169         if (buttonDesc[0].hwnd) {\r
7170           SetFocus(buttonDesc[0].hwnd);\r
7171         } else {\r
7172           SetFocus(hwndMain);\r
7173         }\r
7174       }\r
7175       return 0;\r
7176     case '\023': /* Ctrl+S */\r
7177       sendNextChar = TRUE;\r
7178       return 0;\r
7179     case '\021': /* Ctrl+Q */\r
7180       quoteNextChar = TRUE;\r
7181       return 0;\r
7182     JAWS_REPLAY\r
7183     default:\r
7184       break;\r
7185     }\r
7186     break;\r
7187   case WM_KEYDOWN:\r
7188     switch (wParam) {\r
7189     case VK_UP:\r
7190       GetWindowText(hwnd, buf, MSG_SIZ);\r
7191       p = PrevInHistory(buf);\r
7192       if (p != NULL) {\r
7193         SetWindowText(hwnd, p);\r
7194         sel.cpMin = 999999;\r
7195         sel.cpMax = 999999;\r
7196         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7197         return 0;\r
7198       }\r
7199       break;\r
7200     case VK_DOWN:\r
7201       p = NextInHistory();\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_HOME:\r
7211     case VK_END:\r
7212       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7213       /* fall thru */\r
7214     case VK_PRIOR:\r
7215     case VK_NEXT:\r
7216       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7217       return 0;\r
7218     }\r
7219     break;\r
7220   case WM_MBUTTONDOWN:\r
7221     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7222       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7223     break;\r
7224   case WM_RBUTTONUP:\r
7225     if (GetKeyState(VK_SHIFT) & ~1) {\r
7226       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7227         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7228     } else {\r
7229       POINT pt;\r
7230       HMENU hmenu;\r
7231       hmenu = LoadMenu(hInst, "InputMenu");\r
7232       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7233       if (sel.cpMin == sel.cpMax) {\r
7234         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7235         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7236       }\r
7237       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7238         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7239       }\r
7240       pt.x = LOWORD(lParam);\r
7241       pt.y = HIWORD(lParam);\r
7242       MenuPopup(hwnd, pt, hmenu, -1);\r
7243     }\r
7244     return 0;\r
7245   case WM_COMMAND:\r
7246     switch (LOWORD(wParam)) { \r
7247     case IDM_Undo:\r
7248       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7249       return 0;\r
7250     case IDM_SelectAll:\r
7251       sel.cpMin = 0;\r
7252       sel.cpMax = -1; /*999999?*/\r
7253       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7254       return 0;\r
7255     case IDM_Cut:\r
7256       SendMessage(hwnd, WM_CUT, 0, 0);\r
7257       return 0;\r
7258     case IDM_Paste:\r
7259       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7260       return 0;\r
7261     case IDM_Copy:\r
7262       SendMessage(hwnd, WM_COPY, 0, 0);\r
7263       return 0;\r
7264     }\r
7265     break;\r
7266   }\r
7267   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7268 }\r
7269 \r
7270 #define CO_MAX  100000\r
7271 #define CO_TRIM   1000\r
7272 \r
7273 LRESULT CALLBACK\r
7274 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7275 {\r
7276   static SnapData sd;\r
7277   HWND hText, hInput;\r
7278   RECT rect;\r
7279   static int sizeX, sizeY;\r
7280   int newSizeX, newSizeY;\r
7281   MINMAXINFO *mmi;\r
7282   WORD wMask;\r
7283 \r
7284   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7285   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7286 \r
7287   switch (message) {\r
7288   case WM_NOTIFY:\r
7289     if (((NMHDR*)lParam)->code == EN_LINK)\r
7290     {\r
7291       ENLINK *pLink = (ENLINK*)lParam;\r
7292       if (pLink->msg == WM_LBUTTONUP)\r
7293       {\r
7294         TEXTRANGE tr;\r
7295 \r
7296         tr.chrg = pLink->chrg;\r
7297         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7298         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7299         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7300         free(tr.lpstrText);\r
7301       }\r
7302     }\r
7303     break;\r
7304   case WM_INITDIALOG: /* message: initialize dialog box */\r
7305     hwndConsole = hDlg;\r
7306     SetFocus(hInput);\r
7307     consoleTextWindowProc = (WNDPROC)\r
7308       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7309     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7310     consoleInputWindowProc = (WNDPROC)\r
7311       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7312     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7313     Colorize(ColorNormal, TRUE);\r
7314     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7315     ChangedConsoleFont();\r
7316     GetClientRect(hDlg, &rect);\r
7317     sizeX = rect.right;\r
7318     sizeY = rect.bottom;\r
7319     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7320         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7321       WINDOWPLACEMENT wp;\r
7322       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7323       wp.length = sizeof(WINDOWPLACEMENT);\r
7324       wp.flags = 0;\r
7325       wp.showCmd = SW_SHOW;\r
7326       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7327       wp.rcNormalPosition.left = wpConsole.x;\r
7328       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7329       wp.rcNormalPosition.top = wpConsole.y;\r
7330       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7331       SetWindowPlacement(hDlg, &wp);\r
7332     }\r
7333 \r
7334    // [HGM] Chessknight's change 2004-07-13\r
7335    else { /* Determine Defaults */\r
7336        WINDOWPLACEMENT wp;\r
7337        wpConsole.x = wpMain.width + 1;\r
7338        wpConsole.y = wpMain.y;\r
7339        wpConsole.width = screenWidth -  wpMain.width;\r
7340        wpConsole.height = wpMain.height;\r
7341        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7342        wp.length = sizeof(WINDOWPLACEMENT);\r
7343        wp.flags = 0;\r
7344        wp.showCmd = SW_SHOW;\r
7345        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7346        wp.rcNormalPosition.left = wpConsole.x;\r
7347        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7348        wp.rcNormalPosition.top = wpConsole.y;\r
7349        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7350        SetWindowPlacement(hDlg, &wp);\r
7351     }\r
7352 \r
7353    // Allow hText to highlight URLs and send notifications on them\r
7354    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7355    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7356    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7357    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7358 \r
7359     return FALSE;\r
7360 \r
7361   case WM_SETFOCUS:\r
7362     SetFocus(hInput);\r
7363     return 0;\r
7364 \r
7365   case WM_CLOSE:\r
7366     ExitEvent(0);\r
7367     /* not reached */\r
7368     break;\r
7369 \r
7370   case WM_SIZE:\r
7371     if (IsIconic(hDlg)) break;\r
7372     newSizeX = LOWORD(lParam);\r
7373     newSizeY = HIWORD(lParam);\r
7374     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7375       RECT rectText, rectInput;\r
7376       POINT pt;\r
7377       int newTextHeight, newTextWidth;\r
7378       GetWindowRect(hText, &rectText);\r
7379       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7380       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7381       if (newTextHeight < 0) {\r
7382         newSizeY += -newTextHeight;\r
7383         newTextHeight = 0;\r
7384       }\r
7385       SetWindowPos(hText, NULL, 0, 0,\r
7386         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7387       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7388       pt.x = rectInput.left;\r
7389       pt.y = rectInput.top + newSizeY - sizeY;\r
7390       ScreenToClient(hDlg, &pt);\r
7391       SetWindowPos(hInput, NULL, \r
7392         pt.x, pt.y, /* needs client coords */   \r
7393         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7394         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7395     }\r
7396     sizeX = newSizeX;\r
7397     sizeY = newSizeY;\r
7398     break;\r
7399 \r
7400   case WM_GETMINMAXINFO:\r
7401     /* Prevent resizing window too small */\r
7402     mmi = (MINMAXINFO *) lParam;\r
7403     mmi->ptMinTrackSize.x = 100;\r
7404     mmi->ptMinTrackSize.y = 100;\r
7405     break;\r
7406 \r
7407   /* [AS] Snapping */\r
7408   case WM_ENTERSIZEMOVE:\r
7409     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7410 \r
7411   case WM_SIZING:\r
7412     return OnSizing( &sd, hDlg, wParam, lParam );\r
7413 \r
7414   case WM_MOVING:\r
7415     return OnMoving( &sd, hDlg, wParam, lParam );\r
7416 \r
7417   case WM_EXITSIZEMOVE:\r
7418         UpdateICSWidth(hText);\r
7419     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7420   }\r
7421 \r
7422   return DefWindowProc(hDlg, message, wParam, lParam);\r
7423 }\r
7424 \r
7425 \r
7426 VOID\r
7427 ConsoleCreate()\r
7428 {\r
7429   HWND hCons;\r
7430   if (hwndConsole) return;\r
7431   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7432   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7433 }\r
7434 \r
7435 \r
7436 VOID\r
7437 ConsoleOutput(char* data, int length, int forceVisible)\r
7438 {\r
7439   HWND hText;\r
7440   int trim, exlen;\r
7441   char *p, *q;\r
7442   char buf[CO_MAX+1];\r
7443   POINT pEnd;\r
7444   RECT rect;\r
7445   static int delayLF = 0;\r
7446   CHARRANGE savesel, sel;\r
7447 \r
7448   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7449   p = data;\r
7450   q = buf;\r
7451   if (delayLF) {\r
7452     *q++ = '\r';\r
7453     *q++ = '\n';\r
7454     delayLF = 0;\r
7455   }\r
7456   while (length--) {\r
7457     if (*p == '\n') {\r
7458       if (*++p) {\r
7459         *q++ = '\r';\r
7460         *q++ = '\n';\r
7461       } else {\r
7462         delayLF = 1;\r
7463       }\r
7464     } else if (*p == '\007') {\r
7465        MyPlaySound(&sounds[(int)SoundBell]);\r
7466        p++;\r
7467     } else {\r
7468       *q++ = *p++;\r
7469     }\r
7470   }\r
7471   *q = NULLCHAR;\r
7472   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7473   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7474   /* Save current selection */\r
7475   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7476   exlen = GetWindowTextLength(hText);\r
7477   /* Find out whether current end of text is visible */\r
7478   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7479   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7480   /* Trim existing text if it's too long */\r
7481   if (exlen + (q - buf) > CO_MAX) {\r
7482     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7483     sel.cpMin = 0;\r
7484     sel.cpMax = trim;\r
7485     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7486     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7487     exlen -= trim;\r
7488     savesel.cpMin -= trim;\r
7489     savesel.cpMax -= trim;\r
7490     if (exlen < 0) exlen = 0;\r
7491     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7492     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7493   }\r
7494   /* Append the new text */\r
7495   sel.cpMin = exlen;\r
7496   sel.cpMax = exlen;\r
7497   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7498   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7499   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7500   if (forceVisible || exlen == 0 ||\r
7501       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7502        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7503     /* Scroll to make new end of text visible if old end of text\r
7504        was visible or new text is an echo of user typein */\r
7505     sel.cpMin = 9999999;\r
7506     sel.cpMax = 9999999;\r
7507     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7508     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7509     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7510     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7511   }\r
7512   if (savesel.cpMax == exlen || forceVisible) {\r
7513     /* Move insert point to new end of text if it was at the old\r
7514        end of text or if the 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   } else {\r
7519     /* Restore previous selection */\r
7520     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7521   }\r
7522   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7523 }\r
7524 \r
7525 /*---------*/\r
7526 \r
7527 \r
7528 void\r
7529 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7530 {\r
7531   char buf[100];\r
7532   char *str;\r
7533   COLORREF oldFg, oldBg;\r
7534   HFONT oldFont;\r
7535   RECT rect;\r
7536 \r
7537   if(copyNumber > 1)\r
7538     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7539 \r
7540   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7541   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7542   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7543 \r
7544   rect.left = x;\r
7545   rect.right = x + squareSize;\r
7546   rect.top  = y;\r
7547   rect.bottom = y + squareSize;\r
7548   str = buf;\r
7549 \r
7550   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7551                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7552              y, ETO_CLIPPED|ETO_OPAQUE,\r
7553              &rect, str, strlen(str), NULL);\r
7554 \r
7555   (void) SetTextColor(hdc, oldFg);\r
7556   (void) SetBkColor(hdc, oldBg);\r
7557   (void) SelectObject(hdc, oldFont);\r
7558 }\r
7559 \r
7560 void\r
7561 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7562               RECT *rect, char *color, char *flagFell)\r
7563 {\r
7564   char buf[100];\r
7565   char *str;\r
7566   COLORREF oldFg, oldBg;\r
7567   HFONT oldFont;\r
7568 \r
7569   if (twoBoards && partnerUp) return;\r
7570   if (appData.clockMode) {\r
7571     if (tinyLayout)\r
7572       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7573     else\r
7574       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7575     str = buf;\r
7576   } else {\r
7577     str = color;\r
7578   }\r
7579 \r
7580   if (highlight) {\r
7581     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7582     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7583   } else {\r
7584     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7585     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7586   }\r
7587   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7588 \r
7589   JAWS_SILENCE\r
7590 \r
7591   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7592              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7593              rect, str, strlen(str), NULL);\r
7594   if(logoHeight > 0 && appData.clockMode) {\r
7595       RECT r;\r
7596       str += strlen(color)+2;\r
7597       r.top = rect->top + logoHeight/2;\r
7598       r.left = rect->left;\r
7599       r.right = rect->right;\r
7600       r.bottom = rect->bottom;\r
7601       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7602                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7603                  &r, str, strlen(str), NULL);\r
7604   }\r
7605   (void) SetTextColor(hdc, oldFg);\r
7606   (void) SetBkColor(hdc, oldBg);\r
7607   (void) SelectObject(hdc, oldFont);\r
7608 }\r
7609 \r
7610 \r
7611 int\r
7612 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7613            OVERLAPPED *ovl)\r
7614 {\r
7615   int ok, err;\r
7616 \r
7617   /* [AS]  */\r
7618   if( count <= 0 ) {\r
7619     if (appData.debugMode) {\r
7620       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7621     }\r
7622 \r
7623     return ERROR_INVALID_USER_BUFFER;\r
7624   }\r
7625 \r
7626   ResetEvent(ovl->hEvent);\r
7627   ovl->Offset = ovl->OffsetHigh = 0;\r
7628   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7629   if (ok) {\r
7630     err = NO_ERROR;\r
7631   } else {\r
7632     err = GetLastError();\r
7633     if (err == ERROR_IO_PENDING) {\r
7634       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7635       if (ok)\r
7636         err = NO_ERROR;\r
7637       else\r
7638         err = GetLastError();\r
7639     }\r
7640   }\r
7641   return err;\r
7642 }\r
7643 \r
7644 int\r
7645 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7646             OVERLAPPED *ovl)\r
7647 {\r
7648   int ok, err;\r
7649 \r
7650   ResetEvent(ovl->hEvent);\r
7651   ovl->Offset = ovl->OffsetHigh = 0;\r
7652   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7653   if (ok) {\r
7654     err = NO_ERROR;\r
7655   } else {\r
7656     err = GetLastError();\r
7657     if (err == ERROR_IO_PENDING) {\r
7658       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7659       if (ok)\r
7660         err = NO_ERROR;\r
7661       else\r
7662         err = GetLastError();\r
7663     }\r
7664   }\r
7665   return err;\r
7666 }\r
7667 \r
7668 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7669 void CheckForInputBufferFull( InputSource * is )\r
7670 {\r
7671     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7672         /* Look for end of line */\r
7673         char * p = is->buf;\r
7674         \r
7675         while( p < is->next && *p != '\n' ) {\r
7676             p++;\r
7677         }\r
7678 \r
7679         if( p >= is->next ) {\r
7680             if (appData.debugMode) {\r
7681                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7682             }\r
7683 \r
7684             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7685             is->count = (DWORD) -1;\r
7686             is->next = is->buf;\r
7687         }\r
7688     }\r
7689 }\r
7690 \r
7691 DWORD\r
7692 InputThread(LPVOID arg)\r
7693 {\r
7694   InputSource *is;\r
7695   OVERLAPPED ovl;\r
7696 \r
7697   is = (InputSource *) arg;\r
7698   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7699   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7700   while (is->hThread != NULL) {\r
7701     is->error = DoReadFile(is->hFile, is->next,\r
7702                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7703                            &is->count, &ovl);\r
7704     if (is->error == NO_ERROR) {\r
7705       is->next += is->count;\r
7706     } else {\r
7707       if (is->error == ERROR_BROKEN_PIPE) {\r
7708         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7709         is->count = 0;\r
7710       } else {\r
7711         is->count = (DWORD) -1;\r
7712         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7713         break; \r
7714       }\r
7715     }\r
7716 \r
7717     CheckForInputBufferFull( is );\r
7718 \r
7719     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7720 \r
7721     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7722 \r
7723     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7724   }\r
7725 \r
7726   CloseHandle(ovl.hEvent);\r
7727   CloseHandle(is->hFile);\r
7728 \r
7729   if (appData.debugMode) {\r
7730     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7731   }\r
7732 \r
7733   return 0;\r
7734 }\r
7735 \r
7736 \r
7737 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7738 DWORD\r
7739 NonOvlInputThread(LPVOID arg)\r
7740 {\r
7741   InputSource *is;\r
7742   char *p, *q;\r
7743   int i;\r
7744   char prev;\r
7745 \r
7746   is = (InputSource *) arg;\r
7747   while (is->hThread != NULL) {\r
7748     is->error = ReadFile(is->hFile, is->next,\r
7749                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7750                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7751     if (is->error == NO_ERROR) {\r
7752       /* Change CRLF to LF */\r
7753       if (is->next > is->buf) {\r
7754         p = is->next - 1;\r
7755         i = is->count + 1;\r
7756       } else {\r
7757         p = is->next;\r
7758         i = is->count;\r
7759       }\r
7760       q = p;\r
7761       prev = NULLCHAR;\r
7762       while (i > 0) {\r
7763         if (prev == '\r' && *p == '\n') {\r
7764           *(q-1) = '\n';\r
7765           is->count--;\r
7766         } else { \r
7767           *q++ = *p;\r
7768         }\r
7769         prev = *p++;\r
7770         i--;\r
7771       }\r
7772       *q = NULLCHAR;\r
7773       is->next = q;\r
7774     } else {\r
7775       if (is->error == ERROR_BROKEN_PIPE) {\r
7776         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7777         is->count = 0; \r
7778       } else {\r
7779         is->count = (DWORD) -1;\r
7780       }\r
7781     }\r
7782 \r
7783     CheckForInputBufferFull( is );\r
7784 \r
7785     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7786 \r
7787     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7788 \r
7789     if (is->count < 0) break;  /* Quit on error */\r
7790   }\r
7791   CloseHandle(is->hFile);\r
7792   return 0;\r
7793 }\r
7794 \r
7795 DWORD\r
7796 SocketInputThread(LPVOID arg)\r
7797 {\r
7798   InputSource *is;\r
7799 \r
7800   is = (InputSource *) arg;\r
7801   while (is->hThread != NULL) {\r
7802     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7803     if ((int)is->count == SOCKET_ERROR) {\r
7804       is->count = (DWORD) -1;\r
7805       is->error = WSAGetLastError();\r
7806     } else {\r
7807       is->error = NO_ERROR;\r
7808       is->next += is->count;\r
7809       if (is->count == 0 && is->second == is) {\r
7810         /* End of file on stderr; quit with no message */\r
7811         break;\r
7812       }\r
7813     }\r
7814     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7815 \r
7816     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7817 \r
7818     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7819   }\r
7820   return 0;\r
7821 }\r
7822 \r
7823 VOID\r
7824 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7825 {\r
7826   InputSource *is;\r
7827 \r
7828   is = (InputSource *) lParam;\r
7829   if (is->lineByLine) {\r
7830     /* Feed in lines one by one */\r
7831     char *p = is->buf;\r
7832     char *q = p;\r
7833     while (q < is->next) {\r
7834       if (*q++ == '\n') {\r
7835         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7836         p = q;\r
7837       }\r
7838     }\r
7839     \r
7840     /* Move any partial line to the start of the buffer */\r
7841     q = is->buf;\r
7842     while (p < is->next) {\r
7843       *q++ = *p++;\r
7844     }\r
7845     is->next = q;\r
7846 \r
7847     if (is->error != NO_ERROR || is->count == 0) {\r
7848       /* Notify backend of the error.  Note: If there was a partial\r
7849          line at the end, it is not flushed through. */\r
7850       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7851     }\r
7852   } else {\r
7853     /* Feed in the whole chunk of input at once */\r
7854     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7855     is->next = is->buf;\r
7856   }\r
7857 }\r
7858 \r
7859 /*---------------------------------------------------------------------------*\\r
7860  *\r
7861  *  Menu enables. Used when setting various modes.\r
7862  *\r
7863 \*---------------------------------------------------------------------------*/\r
7864 \r
7865 typedef struct {\r
7866   int item;\r
7867   int flags;\r
7868 } Enables;\r
7869 \r
7870 VOID\r
7871 GreyRevert(Boolean grey)\r
7872 { // [HGM] vari: for retracting variations in local mode\r
7873   HMENU hmenu = GetMenu(hwndMain);\r
7874   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7875   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7876 }\r
7877 \r
7878 VOID\r
7879 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7880 {\r
7881   while (enab->item > 0) {\r
7882     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7883     enab++;\r
7884   }\r
7885 }\r
7886 \r
7887 Enables gnuEnables[] = {\r
7888   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7889   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7890   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7891   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7892   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7893   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7894   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7895   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7896   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7897   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7898   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7899   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7900   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7901 \r
7902   // Needed to switch from ncp to GNU mode on Engine Load\r
7903   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7904   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7905   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7906   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7907   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7908   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7909   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7910   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7911   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7912   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7913   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7914   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7915   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7916   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7917   { -1, -1 }\r
7918 };\r
7919 \r
7920 Enables icsEnables[] = {\r
7921   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7922   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7923   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7924   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7925   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7926   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7927   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7928   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7929   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7930   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7931   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7932   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7933   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7934   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
7935   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
7936   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7937   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7938   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7939   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7940   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
7941   { -1, -1 }\r
7942 };\r
7943 \r
7944 #if ZIPPY\r
7945 Enables zippyEnables[] = {\r
7946   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7947   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7948   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7949   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7950   { -1, -1 }\r
7951 };\r
7952 #endif\r
7953 \r
7954 Enables ncpEnables[] = {\r
7955   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7956   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7957   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7958   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7959   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7960   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7961   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7962   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7963   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7964   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7965   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7966   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7967   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7968   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7969   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7970   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7971   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7972   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7973   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7974   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7975   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7976   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7977   { -1, -1 }\r
7978 };\r
7979 \r
7980 Enables trainingOnEnables[] = {\r
7981   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7982   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7983   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7984   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7985   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7986   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7987   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7988   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7989   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7990   { -1, -1 }\r
7991 };\r
7992 \r
7993 Enables trainingOffEnables[] = {\r
7994   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7995   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7996   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7997   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7998   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7999   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8000   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8001   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8002   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8003   { -1, -1 }\r
8004 };\r
8005 \r
8006 /* These modify either ncpEnables or gnuEnables */\r
8007 Enables cmailEnables[] = {\r
8008   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8009   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8010   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8011   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8012   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8013   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8014   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8015   { -1, -1 }\r
8016 };\r
8017 \r
8018 Enables machineThinkingEnables[] = {\r
8019   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8020   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8021   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8022   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8023   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8024   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8025   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8026   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8027   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8028   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8029   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8030   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8031   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8032 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8033   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8034   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8035   { -1, -1 }\r
8036 };\r
8037 \r
8038 Enables userThinkingEnables[] = {\r
8039   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8040   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8041   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8042   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8043   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8044   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8045   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8046   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8047   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8048   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8049   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8050   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8051   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8052 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8053   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8054   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8055   { -1, -1 }\r
8056 };\r
8057 \r
8058 /*---------------------------------------------------------------------------*\\r
8059  *\r
8060  *  Front-end interface functions exported by XBoard.\r
8061  *  Functions appear in same order as prototypes in frontend.h.\r
8062  * \r
8063 \*---------------------------------------------------------------------------*/\r
8064 VOID\r
8065 CheckMark(UINT item, int state)\r
8066 {\r
8067     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
8068 }\r
8069 \r
8070 VOID\r
8071 ModeHighlight()\r
8072 {\r
8073   static UINT prevChecked = 0;\r
8074   static int prevPausing = 0;\r
8075   UINT nowChecked;\r
8076 \r
8077   if (pausing != prevPausing) {\r
8078     prevPausing = pausing;\r
8079     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8080                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8081     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8082   }\r
8083 \r
8084   switch (gameMode) {\r
8085   case BeginningOfGame:\r
8086     if (appData.icsActive)\r
8087       nowChecked = IDM_IcsClient;\r
8088     else if (appData.noChessProgram)\r
8089       nowChecked = IDM_EditGame;\r
8090     else\r
8091       nowChecked = IDM_MachineBlack;\r
8092     break;\r
8093   case MachinePlaysBlack:\r
8094     nowChecked = IDM_MachineBlack;\r
8095     break;\r
8096   case MachinePlaysWhite:\r
8097     nowChecked = IDM_MachineWhite;\r
8098     break;\r
8099   case TwoMachinesPlay:\r
8100     nowChecked = IDM_TwoMachines;\r
8101     break;\r
8102   case AnalyzeMode:\r
8103     nowChecked = IDM_AnalysisMode;\r
8104     break;\r
8105   case AnalyzeFile:\r
8106     nowChecked = IDM_AnalyzeFile;\r
8107     break;\r
8108   case EditGame:\r
8109     nowChecked = IDM_EditGame;\r
8110     break;\r
8111   case PlayFromGameFile:\r
8112     nowChecked = IDM_LoadGame;\r
8113     break;\r
8114   case EditPosition:\r
8115     nowChecked = IDM_EditPosition;\r
8116     break;\r
8117   case Training:\r
8118     nowChecked = IDM_Training;\r
8119     break;\r
8120   case IcsPlayingWhite:\r
8121   case IcsPlayingBlack:\r
8122   case IcsObserving:\r
8123   case IcsIdle:\r
8124     nowChecked = IDM_IcsClient;\r
8125     break;\r
8126   default:\r
8127   case EndOfGame:\r
8128     nowChecked = 0;\r
8129     break;\r
8130   }\r
8131   CheckMark(prevChecked, MF_UNCHECKED);\r
8132   CheckMark(nowChecked, MF_CHECKED);\r
8133   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8134 \r
8135   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8136     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8137                           MF_BYCOMMAND|MF_ENABLED);\r
8138   } else {\r
8139     (void) EnableMenuItem(GetMenu(hwndMain), \r
8140                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8141   }\r
8142 \r
8143   prevChecked = nowChecked;\r
8144 \r
8145   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8146   if (appData.icsActive) {\r
8147        if (appData.icsEngineAnalyze) {\r
8148                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8149        } else {\r
8150                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8151        }\r
8152   }\r
8153   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8154 }\r
8155 \r
8156 VOID\r
8157 SetICSMode()\r
8158 {\r
8159   HMENU hmenu = GetMenu(hwndMain);\r
8160   SetMenuEnables(hmenu, icsEnables);\r
8161   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8162     MF_BYCOMMAND|MF_ENABLED);\r
8163 #if ZIPPY\r
8164   if (appData.zippyPlay) {\r
8165     SetMenuEnables(hmenu, zippyEnables);\r
8166     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8167          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8168           MF_BYCOMMAND|MF_ENABLED);\r
8169   }\r
8170 #endif\r
8171 }\r
8172 \r
8173 VOID\r
8174 SetGNUMode()\r
8175 {\r
8176   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8177 }\r
8178 \r
8179 VOID\r
8180 SetNCPMode()\r
8181 {\r
8182   HMENU hmenu = GetMenu(hwndMain);\r
8183   SetMenuEnables(hmenu, ncpEnables);\r
8184     DrawMenuBar(hwndMain);\r
8185 }\r
8186 \r
8187 VOID\r
8188 SetCmailMode()\r
8189 {\r
8190   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8191 }\r
8192 \r
8193 VOID \r
8194 SetTrainingModeOn()\r
8195 {\r
8196   int i;\r
8197   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8198   for (i = 0; i < N_BUTTONS; i++) {\r
8199     if (buttonDesc[i].hwnd != NULL)\r
8200       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8201   }\r
8202   CommentPopDown();\r
8203 }\r
8204 \r
8205 VOID SetTrainingModeOff()\r
8206 {\r
8207   int i;\r
8208   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8209   for (i = 0; i < N_BUTTONS; i++) {\r
8210     if (buttonDesc[i].hwnd != NULL)\r
8211       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8212   }\r
8213 }\r
8214 \r
8215 \r
8216 VOID\r
8217 SetUserThinkingEnables()\r
8218 {\r
8219   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8220 }\r
8221 \r
8222 VOID\r
8223 SetMachineThinkingEnables()\r
8224 {\r
8225   HMENU hMenu = GetMenu(hwndMain);\r
8226   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8227 \r
8228   SetMenuEnables(hMenu, machineThinkingEnables);\r
8229 \r
8230   if (gameMode == MachinePlaysBlack) {\r
8231     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8232   } else if (gameMode == MachinePlaysWhite) {\r
8233     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8234   } else if (gameMode == TwoMachinesPlay) {\r
8235     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8236   }\r
8237 }\r
8238 \r
8239 \r
8240 VOID\r
8241 DisplayTitle(char *str)\r
8242 {\r
8243   char title[MSG_SIZ], *host;\r
8244   if (str[0] != NULLCHAR) {\r
8245     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8246   } else if (appData.icsActive) {\r
8247     if (appData.icsCommPort[0] != NULLCHAR)\r
8248       host = "ICS";\r
8249     else \r
8250       host = appData.icsHost;\r
8251       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8252   } else if (appData.noChessProgram) {\r
8253     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8254   } else {\r
8255     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8256     strcat(title, ": ");\r
8257     strcat(title, first.tidy);\r
8258   }\r
8259   SetWindowText(hwndMain, title);\r
8260 }\r
8261 \r
8262 \r
8263 VOID\r
8264 DisplayMessage(char *str1, char *str2)\r
8265 {\r
8266   HDC hdc;\r
8267   HFONT oldFont;\r
8268   int remain = MESSAGE_TEXT_MAX - 1;\r
8269   int len;\r
8270 \r
8271   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8272   messageText[0] = NULLCHAR;\r
8273   if (*str1) {\r
8274     len = strlen(str1);\r
8275     if (len > remain) len = remain;\r
8276     strncpy(messageText, str1, len);\r
8277     messageText[len] = NULLCHAR;\r
8278     remain -= len;\r
8279   }\r
8280   if (*str2 && remain >= 2) {\r
8281     if (*str1) {\r
8282       strcat(messageText, "  ");\r
8283       remain -= 2;\r
8284     }\r
8285     len = strlen(str2);\r
8286     if (len > remain) len = remain;\r
8287     strncat(messageText, str2, len);\r
8288   }\r
8289   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8290   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8291 \r
8292   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8293 \r
8294   SAYMACHINEMOVE();\r
8295 \r
8296   hdc = GetDC(hwndMain);\r
8297   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8298   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8299              &messageRect, messageText, strlen(messageText), NULL);\r
8300   (void) SelectObject(hdc, oldFont);\r
8301   (void) ReleaseDC(hwndMain, hdc);\r
8302 }\r
8303 \r
8304 VOID\r
8305 DisplayError(char *str, int error)\r
8306 {\r
8307   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8308   int len;\r
8309 \r
8310   if (error == 0) {\r
8311     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8312   } else {\r
8313     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8314                         NULL, error, LANG_NEUTRAL,\r
8315                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8316     if (len > 0) {\r
8317       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8318     } else {\r
8319       ErrorMap *em = errmap;\r
8320       while (em->err != 0 && em->err != error) em++;\r
8321       if (em->err != 0) {\r
8322         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8323       } else {\r
8324         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8325       }\r
8326     }\r
8327   }\r
8328   \r
8329   ErrorPopUp(_("Error"), buf);\r
8330 }\r
8331 \r
8332 \r
8333 VOID\r
8334 DisplayMoveError(char *str)\r
8335 {\r
8336   fromX = fromY = -1;\r
8337   ClearHighlights();\r
8338   DrawPosition(FALSE, NULL);\r
8339   if (appData.popupMoveErrors) {\r
8340     ErrorPopUp(_("Error"), str);\r
8341   } else {\r
8342     DisplayMessage(str, "");\r
8343     moveErrorMessageUp = TRUE;\r
8344   }\r
8345 }\r
8346 \r
8347 VOID\r
8348 DisplayFatalError(char *str, int error, int exitStatus)\r
8349 {\r
8350   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8351   int len;\r
8352   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8353 \r
8354   if (error != 0) {\r
8355     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8356                         NULL, error, LANG_NEUTRAL,\r
8357                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8358     if (len > 0) {\r
8359       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8360     } else {\r
8361       ErrorMap *em = errmap;\r
8362       while (em->err != 0 && em->err != error) em++;\r
8363       if (em->err != 0) {\r
8364         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8365       } else {\r
8366         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8367       }\r
8368     }\r
8369     str = buf;\r
8370   }\r
8371   if (appData.debugMode) {\r
8372     fprintf(debugFP, "%s: %s\n", label, str);\r
8373   }\r
8374   if (appData.popupExitMessage) {\r
8375     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8376                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8377   }\r
8378   ExitEvent(exitStatus);\r
8379 }\r
8380 \r
8381 \r
8382 VOID\r
8383 DisplayInformation(char *str)\r
8384 {\r
8385   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8386 }\r
8387 \r
8388 \r
8389 VOID\r
8390 DisplayNote(char *str)\r
8391 {\r
8392   ErrorPopUp(_("Note"), str);\r
8393 }\r
8394 \r
8395 \r
8396 typedef struct {\r
8397   char *title, *question, *replyPrefix;\r
8398   ProcRef pr;\r
8399 } QuestionParams;\r
8400 \r
8401 LRESULT CALLBACK\r
8402 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8403 {\r
8404   static QuestionParams *qp;\r
8405   char reply[MSG_SIZ];\r
8406   int len, err;\r
8407 \r
8408   switch (message) {\r
8409   case WM_INITDIALOG:\r
8410     qp = (QuestionParams *) lParam;\r
8411     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8412     Translate(hDlg, DLG_Question);\r
8413     SetWindowText(hDlg, qp->title);\r
8414     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8415     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8416     return FALSE;\r
8417 \r
8418   case WM_COMMAND:\r
8419     switch (LOWORD(wParam)) {\r
8420     case IDOK:\r
8421       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8422       if (*reply) strcat(reply, " ");\r
8423       len = strlen(reply);\r
8424       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8425       strcat(reply, "\n");\r
8426       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8427       EndDialog(hDlg, TRUE);\r
8428       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8429       return TRUE;\r
8430     case IDCANCEL:\r
8431       EndDialog(hDlg, FALSE);\r
8432       return TRUE;\r
8433     default:\r
8434       break;\r
8435     }\r
8436     break;\r
8437   }\r
8438   return FALSE;\r
8439 }\r
8440 \r
8441 VOID\r
8442 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8443 {\r
8444     QuestionParams qp;\r
8445     FARPROC lpProc;\r
8446     \r
8447     qp.title = title;\r
8448     qp.question = question;\r
8449     qp.replyPrefix = replyPrefix;\r
8450     qp.pr = pr;\r
8451     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8452     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8453       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8454     FreeProcInstance(lpProc);\r
8455 }\r
8456 \r
8457 /* [AS] Pick FRC position */\r
8458 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8459 {\r
8460     static int * lpIndexFRC;\r
8461     BOOL index_is_ok;\r
8462     char buf[16];\r
8463 \r
8464     switch( message )\r
8465     {\r
8466     case WM_INITDIALOG:\r
8467         lpIndexFRC = (int *) lParam;\r
8468 \r
8469         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8470         Translate(hDlg, DLG_NewGameFRC);\r
8471 \r
8472         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8473         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8474         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8475         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8476 \r
8477         break;\r
8478 \r
8479     case WM_COMMAND:\r
8480         switch( LOWORD(wParam) ) {\r
8481         case IDOK:\r
8482             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8483             EndDialog( hDlg, 0 );\r
8484             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8485             return TRUE;\r
8486         case IDCANCEL:\r
8487             EndDialog( hDlg, 1 );   \r
8488             return TRUE;\r
8489         case IDC_NFG_Edit:\r
8490             if( HIWORD(wParam) == EN_CHANGE ) {\r
8491                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8492 \r
8493                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8494             }\r
8495             return TRUE;\r
8496         case IDC_NFG_Random:\r
8497           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8498             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8499             return TRUE;\r
8500         }\r
8501 \r
8502         break;\r
8503     }\r
8504 \r
8505     return FALSE;\r
8506 }\r
8507 \r
8508 int NewGameFRC()\r
8509 {\r
8510     int result;\r
8511     int index = appData.defaultFrcPosition;\r
8512     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8513 \r
8514     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8515 \r
8516     if( result == 0 ) {\r
8517         appData.defaultFrcPosition = index;\r
8518     }\r
8519 \r
8520     return result;\r
8521 }\r
8522 \r
8523 /* [AS] Game list options. Refactored by HGM */\r
8524 \r
8525 HWND gameListOptionsDialog;\r
8526 \r
8527 // low-level front-end: clear text edit / list widget\r
8528 void\r
8529 GLT_ClearList()\r
8530 {\r
8531     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8532 }\r
8533 \r
8534 // low-level front-end: clear text edit / list widget\r
8535 void\r
8536 GLT_DeSelectList()\r
8537 {\r
8538     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8539 }\r
8540 \r
8541 // low-level front-end: append line to text edit / list widget\r
8542 void\r
8543 GLT_AddToList( char *name )\r
8544 {\r
8545     if( name != 0 ) {\r
8546             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8547     }\r
8548 }\r
8549 \r
8550 // low-level front-end: get line from text edit / list widget\r
8551 Boolean\r
8552 GLT_GetFromList( int index, char *name )\r
8553 {\r
8554     if( name != 0 ) {\r
8555             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8556                 return TRUE;\r
8557     }\r
8558     return FALSE;\r
8559 }\r
8560 \r
8561 void GLT_MoveSelection( HWND hDlg, int delta )\r
8562 {\r
8563     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8564     int idx2 = idx1 + delta;\r
8565     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8566 \r
8567     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8568         char buf[128];\r
8569 \r
8570         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8571         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8572         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8573         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8574     }\r
8575 }\r
8576 \r
8577 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8578 {\r
8579     switch( message )\r
8580     {\r
8581     case WM_INITDIALOG:\r
8582         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8583         \r
8584         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8585         Translate(hDlg, DLG_GameListOptions);\r
8586 \r
8587         /* Initialize list */\r
8588         GLT_TagsToList( lpUserGLT );\r
8589 \r
8590         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8591 \r
8592         break;\r
8593 \r
8594     case WM_COMMAND:\r
8595         switch( LOWORD(wParam) ) {\r
8596         case IDOK:\r
8597             GLT_ParseList();\r
8598             EndDialog( hDlg, 0 );\r
8599             return TRUE;\r
8600         case IDCANCEL:\r
8601             EndDialog( hDlg, 1 );\r
8602             return TRUE;\r
8603 \r
8604         case IDC_GLT_Default:\r
8605             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8606             return TRUE;\r
8607 \r
8608         case IDC_GLT_Restore:\r
8609             GLT_TagsToList( appData.gameListTags );\r
8610             return TRUE;\r
8611 \r
8612         case IDC_GLT_Up:\r
8613             GLT_MoveSelection( hDlg, -1 );\r
8614             return TRUE;\r
8615 \r
8616         case IDC_GLT_Down:\r
8617             GLT_MoveSelection( hDlg, +1 );\r
8618             return TRUE;\r
8619         }\r
8620 \r
8621         break;\r
8622     }\r
8623 \r
8624     return FALSE;\r
8625 }\r
8626 \r
8627 int GameListOptions()\r
8628 {\r
8629     int result;\r
8630     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8631 \r
8632       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8633 \r
8634     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8635 \r
8636     if( result == 0 ) {\r
8637         /* [AS] Memory leak here! */\r
8638         appData.gameListTags = strdup( lpUserGLT ); \r
8639     }\r
8640 \r
8641     return result;\r
8642 }\r
8643 \r
8644 VOID\r
8645 DisplayIcsInteractionTitle(char *str)\r
8646 {\r
8647   char consoleTitle[MSG_SIZ];\r
8648 \r
8649     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8650     SetWindowText(hwndConsole, consoleTitle);\r
8651 \r
8652     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8653       char buf[MSG_SIZ], *p = buf, *q;\r
8654         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8655       do {\r
8656         q = strchr(p, ';');\r
8657         if(q) *q++ = 0;\r
8658         if(*p) ChatPopUp(p);\r
8659       } while(p=q);\r
8660     }\r
8661 \r
8662     SetActiveWindow(hwndMain);\r
8663 }\r
8664 \r
8665 void\r
8666 DrawPosition(int fullRedraw, Board board)\r
8667 {\r
8668   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8669 }\r
8670 \r
8671 void NotifyFrontendLogin()\r
8672 {\r
8673         if (hwndConsole)\r
8674                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8675 }\r
8676 \r
8677 VOID\r
8678 ResetFrontEnd()\r
8679 {\r
8680   fromX = fromY = -1;\r
8681   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8682     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8683     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8684     dragInfo.lastpos = dragInfo.pos;\r
8685     dragInfo.start.x = dragInfo.start.y = -1;\r
8686     dragInfo.from = dragInfo.start;\r
8687     ReleaseCapture();\r
8688     DrawPosition(TRUE, NULL);\r
8689   }\r
8690   TagsPopDown();\r
8691 }\r
8692 \r
8693 \r
8694 VOID\r
8695 CommentPopUp(char *title, char *str)\r
8696 {\r
8697   HWND hwnd = GetActiveWindow();\r
8698   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8699   SAY(str);\r
8700   SetActiveWindow(hwnd);\r
8701 }\r
8702 \r
8703 VOID\r
8704 CommentPopDown(void)\r
8705 {\r
8706   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8707   if (commentDialog) {\r
8708     ShowWindow(commentDialog, SW_HIDE);\r
8709   }\r
8710   commentUp = FALSE;\r
8711 }\r
8712 \r
8713 VOID\r
8714 EditCommentPopUp(int index, char *title, char *str)\r
8715 {\r
8716   EitherCommentPopUp(index, title, str, TRUE);\r
8717 }\r
8718 \r
8719 \r
8720 VOID\r
8721 RingBell()\r
8722 {\r
8723   MyPlaySound(&sounds[(int)SoundMove]);\r
8724 }\r
8725 \r
8726 VOID PlayIcsWinSound()\r
8727 {\r
8728   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8729 }\r
8730 \r
8731 VOID PlayIcsLossSound()\r
8732 {\r
8733   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8734 }\r
8735 \r
8736 VOID PlayIcsDrawSound()\r
8737 {\r
8738   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8739 }\r
8740 \r
8741 VOID PlayIcsUnfinishedSound()\r
8742 {\r
8743   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8744 }\r
8745 \r
8746 VOID\r
8747 PlayAlarmSound()\r
8748 {\r
8749   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8750 }\r
8751 \r
8752 VOID\r
8753 PlayTellSound()\r
8754 {\r
8755   MyPlaySound(&textAttribs[ColorTell].sound);\r
8756 }\r
8757 \r
8758 \r
8759 VOID\r
8760 EchoOn()\r
8761 {\r
8762   HWND hInput;\r
8763   consoleEcho = TRUE;\r
8764   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8765   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8766   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8767 }\r
8768 \r
8769 \r
8770 VOID\r
8771 EchoOff()\r
8772 {\r
8773   CHARFORMAT cf;\r
8774   HWND hInput;\r
8775   consoleEcho = FALSE;\r
8776   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8777   /* This works OK: set text and background both to the same color */\r
8778   cf = consoleCF;\r
8779   cf.crTextColor = COLOR_ECHOOFF;\r
8780   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8781   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8782 }\r
8783 \r
8784 /* No Raw()...? */\r
8785 \r
8786 void Colorize(ColorClass cc, int continuation)\r
8787 {\r
8788   currentColorClass = cc;\r
8789   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8790   consoleCF.crTextColor = textAttribs[cc].color;\r
8791   consoleCF.dwEffects = textAttribs[cc].effects;\r
8792   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8793 }\r
8794 \r
8795 char *\r
8796 UserName()\r
8797 {\r
8798   static char buf[MSG_SIZ];\r
8799   DWORD bufsiz = MSG_SIZ;\r
8800 \r
8801   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8802         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8803   }\r
8804   if (!GetUserName(buf, &bufsiz)) {\r
8805     /*DisplayError("Error getting user name", GetLastError());*/\r
8806     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8807   }\r
8808   return buf;\r
8809 }\r
8810 \r
8811 char *\r
8812 HostName()\r
8813 {\r
8814   static char buf[MSG_SIZ];\r
8815   DWORD bufsiz = MSG_SIZ;\r
8816 \r
8817   if (!GetComputerName(buf, &bufsiz)) {\r
8818     /*DisplayError("Error getting host name", GetLastError());*/\r
8819     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8820   }\r
8821   return buf;\r
8822 }\r
8823 \r
8824 \r
8825 int\r
8826 ClockTimerRunning()\r
8827 {\r
8828   return clockTimerEvent != 0;\r
8829 }\r
8830 \r
8831 int\r
8832 StopClockTimer()\r
8833 {\r
8834   if (clockTimerEvent == 0) return FALSE;\r
8835   KillTimer(hwndMain, clockTimerEvent);\r
8836   clockTimerEvent = 0;\r
8837   return TRUE;\r
8838 }\r
8839 \r
8840 void\r
8841 StartClockTimer(long millisec)\r
8842 {\r
8843   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8844                              (UINT) millisec, NULL);\r
8845 }\r
8846 \r
8847 void\r
8848 DisplayWhiteClock(long timeRemaining, int highlight)\r
8849 {\r
8850   HDC hdc;\r
8851   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8852 \r
8853   if(appData.noGUI) return;\r
8854   hdc = GetDC(hwndMain);\r
8855   if (!IsIconic(hwndMain)) {\r
8856     DisplayAClock(hdc, timeRemaining, highlight, \r
8857                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8858   }\r
8859   if (highlight && iconCurrent == iconBlack) {\r
8860     iconCurrent = iconWhite;\r
8861     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8862     if (IsIconic(hwndMain)) {\r
8863       DrawIcon(hdc, 2, 2, iconCurrent);\r
8864     }\r
8865   }\r
8866   (void) ReleaseDC(hwndMain, hdc);\r
8867   if (hwndConsole)\r
8868     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8869 }\r
8870 \r
8871 void\r
8872 DisplayBlackClock(long timeRemaining, int highlight)\r
8873 {\r
8874   HDC hdc;\r
8875   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8876 \r
8877   if(appData.noGUI) return;\r
8878   hdc = GetDC(hwndMain);\r
8879   if (!IsIconic(hwndMain)) {\r
8880     DisplayAClock(hdc, timeRemaining, highlight, \r
8881                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8882   }\r
8883   if (highlight && iconCurrent == iconWhite) {\r
8884     iconCurrent = iconBlack;\r
8885     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8886     if (IsIconic(hwndMain)) {\r
8887       DrawIcon(hdc, 2, 2, iconCurrent);\r
8888     }\r
8889   }\r
8890   (void) ReleaseDC(hwndMain, hdc);\r
8891   if (hwndConsole)\r
8892     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8893 }\r
8894 \r
8895 \r
8896 int\r
8897 LoadGameTimerRunning()\r
8898 {\r
8899   return loadGameTimerEvent != 0;\r
8900 }\r
8901 \r
8902 int\r
8903 StopLoadGameTimer()\r
8904 {\r
8905   if (loadGameTimerEvent == 0) return FALSE;\r
8906   KillTimer(hwndMain, loadGameTimerEvent);\r
8907   loadGameTimerEvent = 0;\r
8908   return TRUE;\r
8909 }\r
8910 \r
8911 void\r
8912 StartLoadGameTimer(long millisec)\r
8913 {\r
8914   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8915                                 (UINT) millisec, NULL);\r
8916 }\r
8917 \r
8918 void\r
8919 AutoSaveGame()\r
8920 {\r
8921   char *defName;\r
8922   FILE *f;\r
8923   char fileTitle[MSG_SIZ];\r
8924 \r
8925   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8926   f = OpenFileDialog(hwndMain, "a", defName,\r
8927                      appData.oldSaveStyle ? "gam" : "pgn",\r
8928                      GAME_FILT, \r
8929                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8930   if (f != NULL) {\r
8931     SaveGame(f, 0, "");\r
8932     fclose(f);\r
8933   }\r
8934 }\r
8935 \r
8936 \r
8937 void\r
8938 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8939 {\r
8940   if (delayedTimerEvent != 0) {\r
8941     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8942       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8943     }\r
8944     KillTimer(hwndMain, delayedTimerEvent);\r
8945     delayedTimerEvent = 0;\r
8946     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8947     delayedTimerCallback();\r
8948   }\r
8949   delayedTimerCallback = cb;\r
8950   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8951                                 (UINT) millisec, NULL);\r
8952 }\r
8953 \r
8954 DelayedEventCallback\r
8955 GetDelayedEvent()\r
8956 {\r
8957   if (delayedTimerEvent) {\r
8958     return delayedTimerCallback;\r
8959   } else {\r
8960     return NULL;\r
8961   }\r
8962 }\r
8963 \r
8964 void\r
8965 CancelDelayedEvent()\r
8966 {\r
8967   if (delayedTimerEvent) {\r
8968     KillTimer(hwndMain, delayedTimerEvent);\r
8969     delayedTimerEvent = 0;\r
8970   }\r
8971 }\r
8972 \r
8973 DWORD GetWin32Priority(int nice)\r
8974 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8975 /*\r
8976 REALTIME_PRIORITY_CLASS     0x00000100\r
8977 HIGH_PRIORITY_CLASS         0x00000080\r
8978 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8979 NORMAL_PRIORITY_CLASS       0x00000020\r
8980 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8981 IDLE_PRIORITY_CLASS         0x00000040\r
8982 */\r
8983         if (nice < -15) return 0x00000080;\r
8984         if (nice < 0)   return 0x00008000;\r
8985         if (nice == 0)  return 0x00000020;\r
8986         if (nice < 15)  return 0x00004000;\r
8987         return 0x00000040;\r
8988 }\r
8989 \r
8990 void RunCommand(char *cmdLine)\r
8991 {\r
8992   /* Now create the child process. */\r
8993   STARTUPINFO siStartInfo;\r
8994   PROCESS_INFORMATION piProcInfo;\r
8995 \r
8996   siStartInfo.cb = sizeof(STARTUPINFO);\r
8997   siStartInfo.lpReserved = NULL;\r
8998   siStartInfo.lpDesktop = NULL;\r
8999   siStartInfo.lpTitle = NULL;\r
9000   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9001   siStartInfo.cbReserved2 = 0;\r
9002   siStartInfo.lpReserved2 = NULL;\r
9003   siStartInfo.hStdInput = NULL;\r
9004   siStartInfo.hStdOutput = NULL;\r
9005   siStartInfo.hStdError = NULL;\r
9006 \r
9007   CreateProcess(NULL,\r
9008                 cmdLine,           /* command line */\r
9009                 NULL,      /* process security attributes */\r
9010                 NULL,      /* primary thread security attrs */\r
9011                 TRUE,      /* handles are inherited */\r
9012                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9013                 NULL,      /* use parent's environment */\r
9014                 NULL,\r
9015                 &siStartInfo, /* STARTUPINFO pointer */\r
9016                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
9017 \r
9018   CloseHandle(piProcInfo.hThread);\r
9019 }\r
9020 \r
9021 /* Start a child process running the given program.\r
9022    The process's standard output can be read from "from", and its\r
9023    standard input can be written to "to".\r
9024    Exit with fatal error if anything goes wrong.\r
9025    Returns an opaque pointer that can be used to destroy the process\r
9026    later.\r
9027 */\r
9028 int\r
9029 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9030 {\r
9031 #define BUFSIZE 4096\r
9032 \r
9033   HANDLE hChildStdinRd, hChildStdinWr,\r
9034     hChildStdoutRd, hChildStdoutWr;\r
9035   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9036   SECURITY_ATTRIBUTES saAttr;\r
9037   BOOL fSuccess;\r
9038   PROCESS_INFORMATION piProcInfo;\r
9039   STARTUPINFO siStartInfo;\r
9040   ChildProc *cp;\r
9041   char buf[MSG_SIZ];\r
9042   DWORD err;\r
9043 \r
9044   if (appData.debugMode) {\r
9045     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9046   }\r
9047 \r
9048   *pr = NoProc;\r
9049 \r
9050   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9051   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9052   saAttr.bInheritHandle = TRUE;\r
9053   saAttr.lpSecurityDescriptor = NULL;\r
9054 \r
9055   /*\r
9056    * The steps for redirecting child's STDOUT:\r
9057    *     1. Create anonymous pipe to be STDOUT for child.\r
9058    *     2. Create a noninheritable duplicate of read handle,\r
9059    *         and close the inheritable read handle.\r
9060    */\r
9061 \r
9062   /* Create a pipe for the child's STDOUT. */\r
9063   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9064     return GetLastError();\r
9065   }\r
9066 \r
9067   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9068   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9069                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9070                              FALSE,     /* not inherited */\r
9071                              DUPLICATE_SAME_ACCESS);\r
9072   if (! fSuccess) {\r
9073     return GetLastError();\r
9074   }\r
9075   CloseHandle(hChildStdoutRd);\r
9076 \r
9077   /*\r
9078    * The steps for redirecting child's STDIN:\r
9079    *     1. Create anonymous pipe to be STDIN for child.\r
9080    *     2. Create a noninheritable duplicate of write handle,\r
9081    *         and close the inheritable write handle.\r
9082    */\r
9083 \r
9084   /* Create a pipe for the child's STDIN. */\r
9085   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9086     return GetLastError();\r
9087   }\r
9088 \r
9089   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9090   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9091                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9092                              FALSE,     /* not inherited */\r
9093                              DUPLICATE_SAME_ACCESS);\r
9094   if (! fSuccess) {\r
9095     return GetLastError();\r
9096   }\r
9097   CloseHandle(hChildStdinWr);\r
9098 \r
9099   /* Arrange to (1) look in dir for the child .exe file, and\r
9100    * (2) have dir be the child's working directory.  Interpret\r
9101    * dir relative to the directory WinBoard loaded from. */\r
9102   GetCurrentDirectory(MSG_SIZ, buf);\r
9103   SetCurrentDirectory(installDir);\r
9104   SetCurrentDirectory(dir);\r
9105 \r
9106   /* Now create the child process. */\r
9107 \r
9108   siStartInfo.cb = sizeof(STARTUPINFO);\r
9109   siStartInfo.lpReserved = NULL;\r
9110   siStartInfo.lpDesktop = NULL;\r
9111   siStartInfo.lpTitle = NULL;\r
9112   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9113   siStartInfo.cbReserved2 = 0;\r
9114   siStartInfo.lpReserved2 = NULL;\r
9115   siStartInfo.hStdInput = hChildStdinRd;\r
9116   siStartInfo.hStdOutput = hChildStdoutWr;\r
9117   siStartInfo.hStdError = hChildStdoutWr;\r
9118 \r
9119   fSuccess = CreateProcess(NULL,\r
9120                            cmdLine,        /* command line */\r
9121                            NULL,           /* process security attributes */\r
9122                            NULL,           /* primary thread security attrs */\r
9123                            TRUE,           /* handles are inherited */\r
9124                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9125                            NULL,           /* use parent's environment */\r
9126                            NULL,\r
9127                            &siStartInfo, /* STARTUPINFO pointer */\r
9128                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9129 \r
9130   err = GetLastError();\r
9131   SetCurrentDirectory(buf); /* return to prev directory */\r
9132   if (! fSuccess) {\r
9133     return err;\r
9134   }\r
9135 \r
9136   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9137     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9138     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9139   }\r
9140 \r
9141   /* Close the handles we don't need in the parent */\r
9142   CloseHandle(piProcInfo.hThread);\r
9143   CloseHandle(hChildStdinRd);\r
9144   CloseHandle(hChildStdoutWr);\r
9145 \r
9146   /* Prepare return value */\r
9147   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9148   cp->kind = CPReal;\r
9149   cp->hProcess = piProcInfo.hProcess;\r
9150   cp->pid = piProcInfo.dwProcessId;\r
9151   cp->hFrom = hChildStdoutRdDup;\r
9152   cp->hTo = hChildStdinWrDup;\r
9153 \r
9154   *pr = (void *) cp;\r
9155 \r
9156   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9157      2000 where engines sometimes don't see the initial command(s)\r
9158      from WinBoard and hang.  I don't understand how that can happen,\r
9159      but the Sleep is harmless, so I've put it in.  Others have also\r
9160      reported what may be the same problem, so hopefully this will fix\r
9161      it for them too.  */\r
9162   Sleep(500);\r
9163 \r
9164   return NO_ERROR;\r
9165 }\r
9166 \r
9167 \r
9168 void\r
9169 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9170 {\r
9171   ChildProc *cp; int result;\r
9172 \r
9173   cp = (ChildProc *) pr;\r
9174   if (cp == NULL) return;\r
9175 \r
9176   switch (cp->kind) {\r
9177   case CPReal:\r
9178     /* TerminateProcess is considered harmful, so... */\r
9179     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9180     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9181     /* The following doesn't work because the chess program\r
9182        doesn't "have the same console" as WinBoard.  Maybe\r
9183        we could arrange for this even though neither WinBoard\r
9184        nor the chess program uses a console for stdio? */\r
9185     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9186 \r
9187     /* [AS] Special termination modes for misbehaving programs... */\r
9188     if( signal == 9 ) { \r
9189         result = TerminateProcess( cp->hProcess, 0 );\r
9190 \r
9191         if ( appData.debugMode) {\r
9192             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9193         }\r
9194     }\r
9195     else if( signal == 10 ) {\r
9196         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9197 \r
9198         if( dw != WAIT_OBJECT_0 ) {\r
9199             result = TerminateProcess( cp->hProcess, 0 );\r
9200 \r
9201             if ( appData.debugMode) {\r
9202                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9203             }\r
9204 \r
9205         }\r
9206     }\r
9207 \r
9208     CloseHandle(cp->hProcess);\r
9209     break;\r
9210 \r
9211   case CPComm:\r
9212     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9213     break;\r
9214 \r
9215   case CPSock:\r
9216     closesocket(cp->sock);\r
9217     WSACleanup();\r
9218     break;\r
9219 \r
9220   case CPRcmd:\r
9221     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9222     closesocket(cp->sock);\r
9223     closesocket(cp->sock2);\r
9224     WSACleanup();\r
9225     break;\r
9226   }\r
9227   free(cp);\r
9228 }\r
9229 \r
9230 void\r
9231 InterruptChildProcess(ProcRef pr)\r
9232 {\r
9233   ChildProc *cp;\r
9234 \r
9235   cp = (ChildProc *) pr;\r
9236   if (cp == NULL) return;\r
9237   switch (cp->kind) {\r
9238   case CPReal:\r
9239     /* The following doesn't work because the chess program\r
9240        doesn't "have the same console" as WinBoard.  Maybe\r
9241        we could arrange for this even though neither WinBoard\r
9242        nor the chess program uses a console for stdio */\r
9243     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9244     break;\r
9245 \r
9246   case CPComm:\r
9247   case CPSock:\r
9248     /* Can't interrupt */\r
9249     break;\r
9250 \r
9251   case CPRcmd:\r
9252     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9253     break;\r
9254   }\r
9255 }\r
9256 \r
9257 \r
9258 int\r
9259 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9260 {\r
9261   char cmdLine[MSG_SIZ];\r
9262 \r
9263   if (port[0] == NULLCHAR) {\r
9264     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9265   } else {\r
9266     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9267   }\r
9268   return StartChildProcess(cmdLine, "", pr);\r
9269 }\r
9270 \r
9271 \r
9272 /* Code to open TCP sockets */\r
9273 \r
9274 int\r
9275 OpenTCP(char *host, char *port, ProcRef *pr)\r
9276 {\r
9277   ChildProc *cp;\r
9278   int err;\r
9279   SOCKET s;\r
9280 \r
9281   struct sockaddr_in sa, mysa;\r
9282   struct hostent FAR *hp;\r
9283   unsigned short uport;\r
9284   WORD wVersionRequested;\r
9285   WSADATA wsaData;\r
9286 \r
9287   /* Initialize socket DLL */\r
9288   wVersionRequested = MAKEWORD(1, 1);\r
9289   err = WSAStartup(wVersionRequested, &wsaData);\r
9290   if (err != 0) return err;\r
9291 \r
9292   /* Make socket */\r
9293   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9294     err = WSAGetLastError();\r
9295     WSACleanup();\r
9296     return err;\r
9297   }\r
9298 \r
9299   /* Bind local address using (mostly) don't-care values.\r
9300    */\r
9301   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9302   mysa.sin_family = AF_INET;\r
9303   mysa.sin_addr.s_addr = INADDR_ANY;\r
9304   uport = (unsigned short) 0;\r
9305   mysa.sin_port = htons(uport);\r
9306   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9307       == SOCKET_ERROR) {\r
9308     err = WSAGetLastError();\r
9309     WSACleanup();\r
9310     return err;\r
9311   }\r
9312 \r
9313   /* Resolve remote host name */\r
9314   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9315   if (!(hp = gethostbyname(host))) {\r
9316     unsigned int b0, b1, b2, b3;\r
9317 \r
9318     err = WSAGetLastError();\r
9319 \r
9320     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9321       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9322       hp->h_addrtype = AF_INET;\r
9323       hp->h_length = 4;\r
9324       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9325       hp->h_addr_list[0] = (char *) malloc(4);\r
9326       hp->h_addr_list[0][0] = (char) b0;\r
9327       hp->h_addr_list[0][1] = (char) b1;\r
9328       hp->h_addr_list[0][2] = (char) b2;\r
9329       hp->h_addr_list[0][3] = (char) b3;\r
9330     } else {\r
9331       WSACleanup();\r
9332       return err;\r
9333     }\r
9334   }\r
9335   sa.sin_family = hp->h_addrtype;\r
9336   uport = (unsigned short) atoi(port);\r
9337   sa.sin_port = htons(uport);\r
9338   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9339 \r
9340   /* Make connection */\r
9341   if (connect(s, (struct sockaddr *) &sa,\r
9342               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9343     err = WSAGetLastError();\r
9344     WSACleanup();\r
9345     return err;\r
9346   }\r
9347 \r
9348   /* Prepare return value */\r
9349   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9350   cp->kind = CPSock;\r
9351   cp->sock = s;\r
9352   *pr = (ProcRef *) cp;\r
9353 \r
9354   return NO_ERROR;\r
9355 }\r
9356 \r
9357 int\r
9358 OpenCommPort(char *name, ProcRef *pr)\r
9359 {\r
9360   HANDLE h;\r
9361   COMMTIMEOUTS ct;\r
9362   ChildProc *cp;\r
9363   char fullname[MSG_SIZ];\r
9364 \r
9365   if (*name != '\\')\r
9366     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9367   else\r
9368     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9369 \r
9370   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9371                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9372   if (h == (HANDLE) -1) {\r
9373     return GetLastError();\r
9374   }\r
9375   hCommPort = h;\r
9376 \r
9377   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9378 \r
9379   /* Accumulate characters until a 100ms pause, then parse */\r
9380   ct.ReadIntervalTimeout = 100;\r
9381   ct.ReadTotalTimeoutMultiplier = 0;\r
9382   ct.ReadTotalTimeoutConstant = 0;\r
9383   ct.WriteTotalTimeoutMultiplier = 0;\r
9384   ct.WriteTotalTimeoutConstant = 0;\r
9385   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9386 \r
9387   /* Prepare return value */\r
9388   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9389   cp->kind = CPComm;\r
9390   cp->hFrom = h;\r
9391   cp->hTo = h;\r
9392   *pr = (ProcRef *) cp;\r
9393 \r
9394   return NO_ERROR;\r
9395 }\r
9396 \r
9397 int\r
9398 OpenLoopback(ProcRef *pr)\r
9399 {\r
9400   DisplayFatalError(_("Not implemented"), 0, 1);\r
9401   return NO_ERROR;\r
9402 }\r
9403 \r
9404 \r
9405 int\r
9406 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9407 {\r
9408   ChildProc *cp;\r
9409   int err;\r
9410   SOCKET s, s2, s3;\r
9411   struct sockaddr_in sa, mysa;\r
9412   struct hostent FAR *hp;\r
9413   unsigned short uport;\r
9414   WORD wVersionRequested;\r
9415   WSADATA wsaData;\r
9416   int fromPort;\r
9417   char stderrPortStr[MSG_SIZ];\r
9418 \r
9419   /* Initialize socket DLL */\r
9420   wVersionRequested = MAKEWORD(1, 1);\r
9421   err = WSAStartup(wVersionRequested, &wsaData);\r
9422   if (err != 0) return err;\r
9423 \r
9424   /* Resolve remote host name */\r
9425   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9426   if (!(hp = gethostbyname(host))) {\r
9427     unsigned int b0, b1, b2, b3;\r
9428 \r
9429     err = WSAGetLastError();\r
9430 \r
9431     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9432       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9433       hp->h_addrtype = AF_INET;\r
9434       hp->h_length = 4;\r
9435       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9436       hp->h_addr_list[0] = (char *) malloc(4);\r
9437       hp->h_addr_list[0][0] = (char) b0;\r
9438       hp->h_addr_list[0][1] = (char) b1;\r
9439       hp->h_addr_list[0][2] = (char) b2;\r
9440       hp->h_addr_list[0][3] = (char) b3;\r
9441     } else {\r
9442       WSACleanup();\r
9443       return err;\r
9444     }\r
9445   }\r
9446   sa.sin_family = hp->h_addrtype;\r
9447   uport = (unsigned short) 514;\r
9448   sa.sin_port = htons(uport);\r
9449   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9450 \r
9451   /* Bind local socket to unused "privileged" port address\r
9452    */\r
9453   s = INVALID_SOCKET;\r
9454   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9455   mysa.sin_family = AF_INET;\r
9456   mysa.sin_addr.s_addr = INADDR_ANY;\r
9457   for (fromPort = 1023;; fromPort--) {\r
9458     if (fromPort < 0) {\r
9459       WSACleanup();\r
9460       return WSAEADDRINUSE;\r
9461     }\r
9462     if (s == INVALID_SOCKET) {\r
9463       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9464         err = WSAGetLastError();\r
9465         WSACleanup();\r
9466         return err;\r
9467       }\r
9468     }\r
9469     uport = (unsigned short) fromPort;\r
9470     mysa.sin_port = htons(uport);\r
9471     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9472         == SOCKET_ERROR) {\r
9473       err = WSAGetLastError();\r
9474       if (err == WSAEADDRINUSE) continue;\r
9475       WSACleanup();\r
9476       return err;\r
9477     }\r
9478     if (connect(s, (struct sockaddr *) &sa,\r
9479       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9480       err = WSAGetLastError();\r
9481       if (err == WSAEADDRINUSE) {\r
9482         closesocket(s);\r
9483         s = -1;\r
9484         continue;\r
9485       }\r
9486       WSACleanup();\r
9487       return err;\r
9488     }\r
9489     break;\r
9490   }\r
9491 \r
9492   /* Bind stderr local socket to unused "privileged" port address\r
9493    */\r
9494   s2 = INVALID_SOCKET;\r
9495   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9496   mysa.sin_family = AF_INET;\r
9497   mysa.sin_addr.s_addr = INADDR_ANY;\r
9498   for (fromPort = 1023;; fromPort--) {\r
9499     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9500     if (fromPort < 0) {\r
9501       (void) closesocket(s);\r
9502       WSACleanup();\r
9503       return WSAEADDRINUSE;\r
9504     }\r
9505     if (s2 == INVALID_SOCKET) {\r
9506       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9507         err = WSAGetLastError();\r
9508         closesocket(s);\r
9509         WSACleanup();\r
9510         return err;\r
9511       }\r
9512     }\r
9513     uport = (unsigned short) fromPort;\r
9514     mysa.sin_port = htons(uport);\r
9515     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9516         == SOCKET_ERROR) {\r
9517       err = WSAGetLastError();\r
9518       if (err == WSAEADDRINUSE) continue;\r
9519       (void) closesocket(s);\r
9520       WSACleanup();\r
9521       return err;\r
9522     }\r
9523     if (listen(s2, 1) == SOCKET_ERROR) {\r
9524       err = WSAGetLastError();\r
9525       if (err == WSAEADDRINUSE) {\r
9526         closesocket(s2);\r
9527         s2 = INVALID_SOCKET;\r
9528         continue;\r
9529       }\r
9530       (void) closesocket(s);\r
9531       (void) closesocket(s2);\r
9532       WSACleanup();\r
9533       return err;\r
9534     }\r
9535     break;\r
9536   }\r
9537   prevStderrPort = fromPort; // remember port used\r
9538   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9539 \r
9540   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9541     err = WSAGetLastError();\r
9542     (void) closesocket(s);\r
9543     (void) closesocket(s2);\r
9544     WSACleanup();\r
9545     return err;\r
9546   }\r
9547 \r
9548   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9549     err = WSAGetLastError();\r
9550     (void) closesocket(s);\r
9551     (void) closesocket(s2);\r
9552     WSACleanup();\r
9553     return err;\r
9554   }\r
9555   if (*user == NULLCHAR) user = UserName();\r
9556   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9557     err = WSAGetLastError();\r
9558     (void) closesocket(s);\r
9559     (void) closesocket(s2);\r
9560     WSACleanup();\r
9561     return err;\r
9562   }\r
9563   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9564     err = WSAGetLastError();\r
9565     (void) closesocket(s);\r
9566     (void) closesocket(s2);\r
9567     WSACleanup();\r
9568     return err;\r
9569   }\r
9570 \r
9571   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9572     err = WSAGetLastError();\r
9573     (void) closesocket(s);\r
9574     (void) closesocket(s2);\r
9575     WSACleanup();\r
9576     return err;\r
9577   }\r
9578   (void) closesocket(s2);  /* Stop listening */\r
9579 \r
9580   /* Prepare return value */\r
9581   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9582   cp->kind = CPRcmd;\r
9583   cp->sock = s;\r
9584   cp->sock2 = s3;\r
9585   *pr = (ProcRef *) cp;\r
9586 \r
9587   return NO_ERROR;\r
9588 }\r
9589 \r
9590 \r
9591 InputSourceRef\r
9592 AddInputSource(ProcRef pr, int lineByLine,\r
9593                InputCallback func, VOIDSTAR closure)\r
9594 {\r
9595   InputSource *is, *is2 = NULL;\r
9596   ChildProc *cp = (ChildProc *) pr;\r
9597 \r
9598   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9599   is->lineByLine = lineByLine;\r
9600   is->func = func;\r
9601   is->closure = closure;\r
9602   is->second = NULL;\r
9603   is->next = is->buf;\r
9604   if (pr == NoProc) {\r
9605     is->kind = CPReal;\r
9606     consoleInputSource = is;\r
9607   } else {\r
9608     is->kind = cp->kind;\r
9609     /* \r
9610         [AS] Try to avoid a race condition if the thread is given control too early:\r
9611         we create all threads suspended so that the is->hThread variable can be\r
9612         safely assigned, then let the threads start with ResumeThread.\r
9613     */\r
9614     switch (cp->kind) {\r
9615     case CPReal:\r
9616       is->hFile = cp->hFrom;\r
9617       cp->hFrom = NULL; /* now owned by InputThread */\r
9618       is->hThread =\r
9619         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9620                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9621       break;\r
9622 \r
9623     case CPComm:\r
9624       is->hFile = cp->hFrom;\r
9625       cp->hFrom = NULL; /* now owned by InputThread */\r
9626       is->hThread =\r
9627         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9628                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9629       break;\r
9630 \r
9631     case CPSock:\r
9632       is->sock = cp->sock;\r
9633       is->hThread =\r
9634         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9635                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9636       break;\r
9637 \r
9638     case CPRcmd:\r
9639       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9640       *is2 = *is;\r
9641       is->sock = cp->sock;\r
9642       is->second = is2;\r
9643       is2->sock = cp->sock2;\r
9644       is2->second = is2;\r
9645       is->hThread =\r
9646         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9647                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9648       is2->hThread =\r
9649         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9650                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9651       break;\r
9652     }\r
9653 \r
9654     if( is->hThread != NULL ) {\r
9655         ResumeThread( is->hThread );\r
9656     }\r
9657 \r
9658     if( is2 != NULL && is2->hThread != NULL ) {\r
9659         ResumeThread( is2->hThread );\r
9660     }\r
9661   }\r
9662 \r
9663   return (InputSourceRef) is;\r
9664 }\r
9665 \r
9666 void\r
9667 RemoveInputSource(InputSourceRef isr)\r
9668 {\r
9669   InputSource *is;\r
9670 \r
9671   is = (InputSource *) isr;\r
9672   is->hThread = NULL;  /* tell thread to stop */\r
9673   CloseHandle(is->hThread);\r
9674   if (is->second != NULL) {\r
9675     is->second->hThread = NULL;\r
9676     CloseHandle(is->second->hThread);\r
9677   }\r
9678 }\r
9679 \r
9680 int no_wrap(char *message, int count)\r
9681 {\r
9682     ConsoleOutput(message, count, FALSE);\r
9683     return count;\r
9684 }\r
9685 \r
9686 int\r
9687 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9688 {\r
9689   DWORD dOutCount;\r
9690   int outCount = SOCKET_ERROR;\r
9691   ChildProc *cp = (ChildProc *) pr;\r
9692   static OVERLAPPED ovl;\r
9693   static int line = 0;\r
9694 \r
9695   if (pr == NoProc)\r
9696   {\r
9697     if (appData.noJoin || !appData.useInternalWrap)\r
9698       return no_wrap(message, count);\r
9699     else\r
9700     {\r
9701       int width = get_term_width();\r
9702       int len = wrap(NULL, message, count, width, &line);\r
9703       char *msg = malloc(len);\r
9704       int dbgchk;\r
9705 \r
9706       if (!msg)\r
9707         return no_wrap(message, count);\r
9708       else\r
9709       {\r
9710         dbgchk = wrap(msg, message, count, width, &line);\r
9711         if (dbgchk != len && appData.debugMode)\r
9712             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9713         ConsoleOutput(msg, len, FALSE);\r
9714         free(msg);\r
9715         return len;\r
9716       }\r
9717     }\r
9718   }\r
9719 \r
9720   if (ovl.hEvent == NULL) {\r
9721     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9722   }\r
9723   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9724 \r
9725   switch (cp->kind) {\r
9726   case CPSock:\r
9727   case CPRcmd:\r
9728     outCount = send(cp->sock, message, count, 0);\r
9729     if (outCount == SOCKET_ERROR) {\r
9730       *outError = WSAGetLastError();\r
9731     } else {\r
9732       *outError = NO_ERROR;\r
9733     }\r
9734     break;\r
9735 \r
9736   case CPReal:\r
9737     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9738                   &dOutCount, NULL)) {\r
9739       *outError = NO_ERROR;\r
9740       outCount = (int) dOutCount;\r
9741     } else {\r
9742       *outError = GetLastError();\r
9743     }\r
9744     break;\r
9745 \r
9746   case CPComm:\r
9747     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9748                             &dOutCount, &ovl);\r
9749     if (*outError == NO_ERROR) {\r
9750       outCount = (int) dOutCount;\r
9751     }\r
9752     break;\r
9753   }\r
9754   return outCount;\r
9755 }\r
9756 \r
9757 void\r
9758 DoSleep(int n)\r
9759 {\r
9760     if(n != 0) Sleep(n);\r
9761 }\r
9762 \r
9763 int\r
9764 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9765                        long msdelay)\r
9766 {\r
9767   /* Ignore delay, not implemented for WinBoard */\r
9768   return OutputToProcess(pr, message, count, outError);\r
9769 }\r
9770 \r
9771 \r
9772 void\r
9773 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9774                         char *buf, int count, int error)\r
9775 {\r
9776   DisplayFatalError(_("Not implemented"), 0, 1);\r
9777 }\r
9778 \r
9779 /* see wgamelist.c for Game List functions */\r
9780 /* see wedittags.c for Edit Tags functions */\r
9781 \r
9782 \r
9783 int\r
9784 ICSInitScript()\r
9785 {\r
9786   FILE *f;\r
9787   char buf[MSG_SIZ];\r
9788   char *dummy;\r
9789 \r
9790   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9791     f = fopen(buf, "r");\r
9792     if (f != NULL) {\r
9793       ProcessICSInitScript(f);\r
9794       fclose(f);\r
9795       return TRUE;\r
9796     }\r
9797   }\r
9798   return FALSE;\r
9799 }\r
9800 \r
9801 \r
9802 VOID\r
9803 StartAnalysisClock()\r
9804 {\r
9805   if (analysisTimerEvent) return;\r
9806   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9807                                         (UINT) 2000, NULL);\r
9808 }\r
9809 \r
9810 VOID\r
9811 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9812 {\r
9813   highlightInfo.sq[0].x = fromX;\r
9814   highlightInfo.sq[0].y = fromY;\r
9815   highlightInfo.sq[1].x = toX;\r
9816   highlightInfo.sq[1].y = toY;\r
9817 }\r
9818 \r
9819 VOID\r
9820 ClearHighlights()\r
9821 {\r
9822   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9823     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9824 }\r
9825 \r
9826 VOID\r
9827 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9828 {\r
9829   premoveHighlightInfo.sq[0].x = fromX;\r
9830   premoveHighlightInfo.sq[0].y = fromY;\r
9831   premoveHighlightInfo.sq[1].x = toX;\r
9832   premoveHighlightInfo.sq[1].y = toY;\r
9833 }\r
9834 \r
9835 VOID\r
9836 ClearPremoveHighlights()\r
9837 {\r
9838   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9839     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9840 }\r
9841 \r
9842 VOID\r
9843 ShutDownFrontEnd()\r
9844 {\r
9845   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9846   DeleteClipboardTempFiles();\r
9847 }\r
9848 \r
9849 void\r
9850 BoardToTop()\r
9851 {\r
9852     if (IsIconic(hwndMain))\r
9853       ShowWindow(hwndMain, SW_RESTORE);\r
9854 \r
9855     SetActiveWindow(hwndMain);\r
9856 }\r
9857 \r
9858 /*\r
9859  * Prototypes for animation support routines\r
9860  */\r
9861 static void ScreenSquare(int column, int row, POINT * pt);\r
9862 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9863      POINT frames[], int * nFrames);\r
9864 \r
9865 \r
9866 #define kFactor 4\r
9867 \r
9868 void\r
9869 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9870 {       // [HGM] atomic: animate blast wave\r
9871         int i;\r
9872 \r
9873         explodeInfo.fromX = fromX;\r
9874         explodeInfo.fromY = fromY;\r
9875         explodeInfo.toX = toX;\r
9876         explodeInfo.toY = toY;\r
9877         for(i=1; i<4*kFactor; i++) {\r
9878             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9879             DrawPosition(FALSE, board);\r
9880             Sleep(appData.animSpeed);\r
9881         }\r
9882         explodeInfo.radius = 0;\r
9883         DrawPosition(TRUE, board);\r
9884 }\r
9885 \r
9886 void\r
9887 AnimateMove(board, fromX, fromY, toX, toY)\r
9888      Board board;\r
9889      int fromX;\r
9890      int fromY;\r
9891      int toX;\r
9892      int toY;\r
9893 {\r
9894   ChessSquare piece;\r
9895   POINT start, finish, mid;\r
9896   POINT frames[kFactor * 2 + 1];\r
9897   int nFrames, n;\r
9898 \r
9899   if (!appData.animate) return;\r
9900   if (doingSizing) return;\r
9901   if (fromY < 0 || fromX < 0) return;\r
9902   piece = board[fromY][fromX];\r
9903   if (piece >= EmptySquare) return;\r
9904 \r
9905   ScreenSquare(fromX, fromY, &start);\r
9906   ScreenSquare(toX, toY, &finish);\r
9907 \r
9908   /* All moves except knight jumps move in straight line */\r
9909   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9910     mid.x = start.x + (finish.x - start.x) / 2;\r
9911     mid.y = start.y + (finish.y - start.y) / 2;\r
9912   } else {\r
9913     /* Knight: make straight movement then diagonal */\r
9914     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9915        mid.x = start.x + (finish.x - start.x) / 2;\r
9916        mid.y = start.y;\r
9917      } else {\r
9918        mid.x = start.x;\r
9919        mid.y = start.y + (finish.y - start.y) / 2;\r
9920      }\r
9921   }\r
9922   \r
9923   /* Don't use as many frames for very short moves */\r
9924   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9925     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9926   else\r
9927     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9928 \r
9929   animInfo.from.x = fromX;\r
9930   animInfo.from.y = fromY;\r
9931   animInfo.to.x = toX;\r
9932   animInfo.to.y = toY;\r
9933   animInfo.lastpos = start;\r
9934   animInfo.piece = piece;\r
9935   for (n = 0; n < nFrames; n++) {\r
9936     animInfo.pos = frames[n];\r
9937     DrawPosition(FALSE, NULL);\r
9938     animInfo.lastpos = animInfo.pos;\r
9939     Sleep(appData.animSpeed);\r
9940   }\r
9941   animInfo.pos = finish;\r
9942   DrawPosition(FALSE, NULL);\r
9943   animInfo.piece = EmptySquare;\r
9944   Explode(board, fromX, fromY, toX, toY);\r
9945 }\r
9946 \r
9947 /*      Convert board position to corner of screen rect and color       */\r
9948 \r
9949 static void\r
9950 ScreenSquare(column, row, pt)\r
9951      int column; int row; POINT * pt;\r
9952 {\r
9953   if (flipView) {\r
9954     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
9955     pt->y = lineGap + row * (squareSize + lineGap) + border;\r
9956   } else {\r
9957     pt->x = lineGap + column * (squareSize + lineGap) + border;\r
9958     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
9959   }\r
9960 }\r
9961 \r
9962 /*      Generate a series of frame coords from start->mid->finish.\r
9963         The movement rate doubles until the half way point is\r
9964         reached, then halves back down to the final destination,\r
9965         which gives a nice slow in/out effect. The algorithmn\r
9966         may seem to generate too many intermediates for short\r
9967         moves, but remember that the purpose is to attract the\r
9968         viewers attention to the piece about to be moved and\r
9969         then to where it ends up. Too few frames would be less\r
9970         noticeable.                                             */\r
9971 \r
9972 static void\r
9973 Tween(start, mid, finish, factor, frames, nFrames)\r
9974      POINT * start; POINT * mid;\r
9975      POINT * finish; int factor;\r
9976      POINT frames[]; int * nFrames;\r
9977 {\r
9978   int n, fraction = 1, count = 0;\r
9979 \r
9980   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9981   for (n = 0; n < factor; n++)\r
9982     fraction *= 2;\r
9983   for (n = 0; n < factor; n++) {\r
9984     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9985     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9986     count ++;\r
9987     fraction = fraction / 2;\r
9988   }\r
9989   \r
9990   /* Midpoint */\r
9991   frames[count] = *mid;\r
9992   count ++;\r
9993   \r
9994   /* Slow out, stepping 1/2, then 1/4, ... */\r
9995   fraction = 2;\r
9996   for (n = 0; n < factor; n++) {\r
9997     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9998     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9999     count ++;\r
10000     fraction = fraction * 2;\r
10001   }\r
10002   *nFrames = count;\r
10003 }\r
10004 \r
10005 void\r
10006 SettingsPopUp(ChessProgramState *cps)\r
10007 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
10008       EngineOptionsPopup(savedHwnd, cps);\r
10009 }\r
10010 \r
10011 int flock(int fid, int code)\r
10012 {\r
10013     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
10014     OVERLAPPED ov;\r
10015     ov.hEvent = NULL;\r
10016     ov.Offset = 0;\r
10017     ov.OffsetHigh = 0;\r
10018     switch(code) {\r
10019       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
10020       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
10021       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
10022       default: return -1;\r
10023     }\r
10024     return 0;\r
10025 }\r
10026 \r
10027 char *\r
10028 Col2Text (int n)\r
10029 {\r
10030     static int i=0;\r
10031     static char col[8][20];\r
10032     COLORREF color = *(COLORREF *) colorVariable[n];\r
10033     i = i+1 & 7;\r
10034     snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
10035     return col[i];\r
10036 }\r
10037 \r
10038 void\r
10039 ActivateTheme (int new)\r
10040 {   // Redo initialization of features depending on options that can occur in themes\r
10041    InitTextures();\r
10042    if(new) InitDrawingColors();\r
10043    fontBitmapSquareSize = 0; // request creation of new font pieces\r
10044    InitDrawingSizes(boardSize, 0);\r
10045    InvalidateRect(hwndMain, NULL, TRUE);\r
10046 }\r