Add final piece count to search criteria
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts.\r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 #include <io.h>\r
77 \r
78 #if __GNUC__\r
79 #include <errno.h>\r
80 #include <string.h>\r
81 #endif\r
82 \r
83 #include "common.h"\r
84 #include "frontend.h"\r
85 #include "backend.h"\r
86 #include "winboard.h"\r
87 #include "moves.h"\r
88 #include "wclipbrd.h"\r
89 #include "woptions.h"\r
90 #include "wsockerr.h"\r
91 #include "defaults.h"\r
92 #include "help.h"\r
93 #include "wsnap.h"\r
94 \r
95 #define SLASH '/'\r
96 #define DATADIR "~~"\r
97 \r
98 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
99 \r
100   int myrandom(void);\r
101   void mysrandom(unsigned int seed);\r
102 \r
103 extern int whiteFlag, blackFlag;\r
104 Boolean flipClock = FALSE;\r
105 extern HANDLE chatHandle[];\r
106 extern enum ICS_TYPE ics_type;\r
107 \r
108 int  MySearchPath P((char *installDir, char *name, char *fullname));\r
109 int  MyGetFullPathName P((char *name, char *fullname));\r
110 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
111 VOID NewVariantPopup(HWND hwnd);\r
112 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
113                    /*char*/int promoChar));\r
114 void DisplayMove P((int moveNumber));\r
115 void ChatPopUp P((char *s));\r
116 typedef struct {\r
117   ChessSquare piece;  \r
118   POINT pos;      /* window coordinates of current pos */\r
119   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
120   POINT from;     /* board coordinates of the piece's orig pos */\r
121   POINT to;       /* board coordinates of the piece's new pos */\r
122 } AnimInfo;\r
123 \r
124 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
125 \r
126 typedef struct {\r
127   POINT start;    /* window coordinates of start pos */\r
128   POINT pos;      /* window coordinates of current pos */\r
129   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
130   POINT from;     /* board coordinates of the piece's orig pos */\r
131   ChessSquare piece;\r
132 } DragInfo;\r
133 \r
134 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
135 \r
136 typedef struct {\r
137   POINT sq[2];    /* board coordinates of from, to squares */\r
138 } HighlightInfo;\r
139 \r
140 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
141 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
142 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
143 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
144 \r
145 typedef struct { // [HGM] atomic\r
146   int fromX, fromY, toX, toY, radius;\r
147 } ExplodeInfo;\r
148 \r
149 static ExplodeInfo explodeInfo;\r
150 \r
151 /* Window class names */\r
152 char szAppName[] = "WinBoard";\r
153 char szConsoleName[] = "WBConsole";\r
154 \r
155 /* Title bar text */\r
156 char szTitle[] = "WinBoard";\r
157 char szConsoleTitle[] = "I C S Interaction";\r
158 \r
159 char *programName;\r
160 char *settingsFileName;\r
161 Boolean saveSettingsOnExit;\r
162 char installDir[MSG_SIZ];\r
163 int errorExitStatus;\r
164 \r
165 BoardSize boardSize;\r
166 Boolean chessProgram;\r
167 //static int boardX, boardY;\r
168 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
169 int squareSize, lineGap, minorSize, border;\r
170 static int winW, winH;\r
171 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
172 static int logoHeight = 0;\r
173 static char messageText[MESSAGE_TEXT_MAX];\r
174 static int clockTimerEvent = 0;\r
175 static int loadGameTimerEvent = 0;\r
176 static int analysisTimerEvent = 0;\r
177 static DelayedEventCallback delayedTimerCallback;\r
178 static int delayedTimerEvent = 0;\r
179 static int buttonCount = 2;\r
180 char *icsTextMenuString;\r
181 char *icsNames;\r
182 char *firstChessProgramNames;\r
183 char *secondChessProgramNames;\r
184 \r
185 #define PALETTESIZE 256\r
186 \r
187 HINSTANCE hInst;          /* current instance */\r
188 Boolean alwaysOnTop = FALSE;\r
189 RECT boardRect;\r
190 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
191   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
192 COLORREF markerColor[8] = { 0x00FFFF, 0x0000FF, 0x00FF00, 0xFF0000, 0xFFFF00, 0xFF00FF, 0xFFFFFF, 0x000000 };\r
193 HPALETTE hPal;\r
194 ColorClass currentColorClass;\r
195 \r
196 static HWND savedHwnd;\r
197 HWND hCommPort = NULL;    /* currently open comm port */\r
198 static HWND hwndPause;    /* pause button */\r
199 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
200 static HBRUSH lightSquareBrush, darkSquareBrush,\r
201   blackSquareBrush, /* [HGM] for band between board and holdings */\r
202   explodeBrush,     /* [HGM] atomic */\r
203   markerBrush[8],   /* [HGM] markers */\r
204   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
205 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
206 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
207 static HPEN gridPen = NULL;\r
208 static HPEN highlightPen = NULL;\r
209 static HPEN premovePen = NULL;\r
210 static NPLOGPALETTE pLogPal;\r
211 static BOOL paletteChanged = FALSE;\r
212 static HICON iconWhite, iconBlack, iconCurrent;\r
213 static int doingSizing = FALSE;\r
214 static int lastSizing = 0;\r
215 static int prevStderrPort;\r
216 static HBITMAP userLogo;\r
217 \r
218 static HBITMAP liteBackTexture = NULL;\r
219 static HBITMAP darkBackTexture = NULL;\r
220 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
221 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
222 static int backTextureSquareSize = 0;\r
223 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
224 \r
225 #if __GNUC__ && !defined(_winmajor)\r
226 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
227 #else\r
228 \r
229 #if defined(_winmajor)\r
230 #define oldDialog (_winmajor < 4)\r
231 #else\r
232 #define oldDialog 0\r
233 #endif\r
234 #endif\r
235 \r
236 #define INTERNATIONAL\r
237 \r
238 #ifdef INTERNATIONAL\r
239 #  define _(s) T_(s)\r
240 #  define N_(s) s\r
241 #else\r
242 #  define _(s) s\r
243 #  define N_(s) s\r
244 #  define T_(s) s\r
245 #  define Translate(x, y)\r
246 #  define LoadLanguageFile(s)\r
247 #endif\r
248 \r
249 #ifdef INTERNATIONAL\r
250 \r
251 Boolean barbaric; // flag indicating if translation is needed\r
252 \r
253 // list of item numbers used in each dialog (used to alter language at run time)\r
254 \r
255 #define ABOUTBOX -1  /* not sure why these are needed */\r
256 #define ABOUTBOX2 -1\r
257 \r
258 int dialogItems[][42] = {\r
259 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
260 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
261   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
262 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,\r
263   OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds,\r
264   OPT_Ranget, IDOK, IDCANCEL }, \r
265 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
266   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
267 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
268 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
269   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
270 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
271 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
272   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
273 { ABOUTBOX2, IDC_ChessBoard }, \r
274 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
275   OPT_GameListClose, IDC_GameListDoFilter }, \r
276 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
277 { DLG_Error, IDOK }, \r
278 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
279   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
280 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
281 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
282   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
283   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
284 { DLG_IndexNumber, IDC_Index }, \r
285 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
286 { DLG_TypeInName, IDOK, IDCANCEL }, \r
287 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
288   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
289 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
290   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
291   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
292   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
293   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
294   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
295   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
296 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
297   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
298   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
299   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
300   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
301   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
302   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
303   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
304   GPB_General, GPB_Alarm, OPT_AutoCreate }, \r
305 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
306   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
307   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
308   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
309   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
310   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
311   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
312   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
313 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
314   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
315   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
316   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
317   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
318   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
319   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
320   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
321   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
322 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
323   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
324   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
325   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
326   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
327 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
328 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
329   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
330 { DLG_MoveHistory }, \r
331 { DLG_EvalGraph }, \r
332 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
333 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
334 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
335   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
336   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
337   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
338 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
339   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
340   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
341 { 0 }\r
342 };\r
343 \r
344 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
345 static int lastChecked;\r
346 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
347 extern int tinyLayout;\r
348 extern char * menuBarText[][10];\r
349 \r
350 void\r
351 LoadLanguageFile(char *name)\r
352 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
353     FILE *f;\r
354     int i=0, j=0, n=0, k;\r
355     char buf[MSG_SIZ];\r
356 \r
357     if(!name || name[0] == NULLCHAR) return;\r
358       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
359     appData.language = oldLanguage;\r
360     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
361     if((f = fopen(buf, "r")) == NULL) return;\r
362     while((k = fgetc(f)) != EOF) {\r
363         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
364         languageBuf[i] = k;\r
365         if(k == '\n') {\r
366             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
367                 char *p;\r
368                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
369                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
370                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
371                         english[j] = languageBuf + n + 1; *p = 0;\r
372                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
373 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
374                     }\r
375                 }\r
376             }\r
377             n = i + 1;\r
378         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
379             switch(k) {\r
380               case 'n': k = '\n'; break;\r
381               case 'r': k = '\r'; break;\r
382               case 't': k = '\t'; break;\r
383             }\r
384             languageBuf[--i] = k;\r
385         }\r
386         i++;\r
387     }\r
388     fclose(f);\r
389     barbaric = (j != 0);\r
390     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
391 }\r
392 \r
393 char *\r
394 T_(char *s)\r
395 {   // return the translation of the given string\r
396     // efficiency can be improved a lot...\r
397     int i=0;\r
398     static char buf[MSG_SIZ];\r
399 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
400     if(!barbaric) return s;\r
401     if(!s) return ""; // sanity\r
402     while(english[i]) {\r
403         if(!strcmp(s, english[i])) return foreign[i];\r
404         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
405             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
406             return buf;\r
407         }\r
408         i++;\r
409     }\r
410     return s;\r
411 }\r
412 \r
413 void\r
414 Translate(HWND hDlg, int dialogID)\r
415 {   // translate all text items in the given dialog\r
416     int i=0, j, k;\r
417     char buf[MSG_SIZ], *s;\r
418     if(!barbaric) return;\r
419     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
420     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
421     GetWindowText( hDlg, buf, MSG_SIZ );\r
422     s = T_(buf);\r
423     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
424     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
425         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
426         if(strlen(buf) == 0) continue;\r
427         s = T_(buf);\r
428         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
429     }\r
430 }\r
431 \r
432 HMENU\r
433 TranslateOneMenu(int i, HMENU subMenu)\r
434 {\r
435     int j;\r
436     static MENUITEMINFO info;\r
437 \r
438     info.cbSize = sizeof(MENUITEMINFO);\r
439     info.fMask = MIIM_STATE | MIIM_TYPE;\r
440           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
441             char buf[MSG_SIZ];\r
442             info.dwTypeData = buf;\r
443             info.cch = sizeof(buf);\r
444             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
445             if(i < 10) {\r
446                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
447                 else menuText[i][j] = strdup(buf); // remember original on first change\r
448             }\r
449             if(buf[0] == NULLCHAR) continue;\r
450             info.dwTypeData = T_(buf);\r
451             info.cch = strlen(buf)+1;\r
452             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
453           }\r
454     return subMenu;\r
455 }\r
456 \r
457 void\r
458 TranslateMenus(int addLanguage)\r
459 {\r
460     int i;\r
461     WIN32_FIND_DATA fileData;\r
462     HANDLE hFind;\r
463 #define IDM_English 1970\r
464     if(1) {\r
465         HMENU mainMenu = GetMenu(hwndMain);\r
466         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
467           HMENU subMenu = GetSubMenu(mainMenu, i);\r
468           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
469                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
470           TranslateOneMenu(i, subMenu);\r
471         }\r
472         DrawMenuBar(hwndMain);\r
473     }\r
474 \r
475     if(!addLanguage) return;\r
476     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
477         HMENU mainMenu = GetMenu(hwndMain);\r
478         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
479         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
480         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
481         i = 0; lastChecked = IDM_English;\r
482         do {\r
483             char *p, *q = fileData.cFileName;\r
484             int checkFlag = MF_UNCHECKED;\r
485             languageFile[i] = strdup(q);\r
486             if(barbaric && !strcmp(oldLanguage, q)) {\r
487                 checkFlag = MF_CHECKED;\r
488                 lastChecked = IDM_English + i + 1;\r
489                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
490             }\r
491             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
492             p = strstr(fileData.cFileName, ".lng");\r
493             if(p) *p = 0;\r
494             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
495         } while(FindNextFile(hFind, &fileData));\r
496         FindClose(hFind);\r
497     }\r
498 }\r
499 \r
500 #endif\r
501 \r
502 #define IDM_RecentEngines 3000\r
503 \r
504 void\r
505 RecentEngineMenu (char *s)\r
506 {\r
507     if(appData.icsActive) return;\r
508     if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
509         HMENU mainMenu = GetMenu(hwndMain);\r
510         HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
511         int i=IDM_RecentEngines;\r
512         recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
513         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
514         while(*s) {\r
515           char *p = strchr(s, '\n');\r
516           if(p == NULL) return; // malformed!\r
517           *p = NULLCHAR;\r
518           AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
519           *p = '\n';\r
520           s = p+1;\r
521         }\r
522     }\r
523 }\r
524 \r
525 \r
526 typedef struct {\r
527   char *name;\r
528   int squareSize;\r
529   int lineGap;\r
530   int smallLayout;\r
531   int tinyLayout;\r
532   int cliWidth, cliHeight;\r
533 } SizeInfo;\r
534 \r
535 SizeInfo sizeInfo[] = \r
536 {\r
537   { "tiny",     21, 0, 1, 1, 0, 0 },\r
538   { "teeny",    25, 1, 1, 1, 0, 0 },\r
539   { "dinky",    29, 1, 1, 1, 0, 0 },\r
540   { "petite",   33, 1, 1, 1, 0, 0 },\r
541   { "slim",     37, 2, 1, 0, 0, 0 },\r
542   { "small",    40, 2, 1, 0, 0, 0 },\r
543   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
544   { "middling", 49, 2, 0, 0, 0, 0 },\r
545   { "average",  54, 2, 0, 0, 0, 0 },\r
546   { "moderate", 58, 3, 0, 0, 0, 0 },\r
547   { "medium",   64, 3, 0, 0, 0, 0 },\r
548   { "bulky",    72, 3, 0, 0, 0, 0 },\r
549   { "large",    80, 3, 0, 0, 0, 0 },\r
550   { "big",      87, 3, 0, 0, 0, 0 },\r
551   { "huge",     95, 3, 0, 0, 0, 0 },\r
552   { "giant",    108, 3, 0, 0, 0, 0 },\r
553   { "colossal", 116, 4, 0, 0, 0, 0 },\r
554   { "titanic",  129, 4, 0, 0, 0, 0 },\r
555   { NULL, 0, 0, 0, 0, 0, 0 }\r
556 };\r
557 \r
558 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
559 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
560 {\r
561   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
562   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
563   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
564   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
565   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
566   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
567   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
568   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
569   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
570   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
571   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
572   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
573   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
574   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
575   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
576   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
577   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL), MF (GAMELIST_FONT_ALL) },\r
578   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
579 };\r
580 \r
581 MyFont *font[NUM_SIZES][NUM_FONTS];\r
582 \r
583 typedef struct {\r
584   char *label;\r
585   int id;\r
586   HWND hwnd;\r
587   WNDPROC wndproc;\r
588 } MyButtonDesc;\r
589 \r
590 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
591 #define N_BUTTONS 5\r
592 \r
593 MyButtonDesc buttonDesc[N_BUTTONS] =\r
594 {\r
595   {"<<", IDM_ToStart, NULL, NULL},\r
596   {"<", IDM_Backward, NULL, NULL},\r
597   {"P", IDM_Pause, NULL, NULL},\r
598   {">", IDM_Forward, NULL, NULL},\r
599   {">>", IDM_ToEnd, NULL, NULL},\r
600 };\r
601 \r
602 int tinyLayout = 0, smallLayout = 0;\r
603 #define MENU_BAR_ITEMS 9\r
604 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
605   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
606   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
607 };\r
608 \r
609 \r
610 MySound sounds[(int)NSoundClasses];\r
611 MyTextAttribs textAttribs[(int)NColorClasses];\r
612 \r
613 MyColorizeAttribs colorizeAttribs[] = {\r
614   { (COLORREF)0, 0, N_("Shout Text") },\r
615   { (COLORREF)0, 0, N_("SShout/CShout") },\r
616   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
617   { (COLORREF)0, 0, N_("Channel Text") },\r
618   { (COLORREF)0, 0, N_("Kibitz Text") },\r
619   { (COLORREF)0, 0, N_("Tell Text") },\r
620   { (COLORREF)0, 0, N_("Challenge Text") },\r
621   { (COLORREF)0, 0, N_("Request Text") },\r
622   { (COLORREF)0, 0, N_("Seek Text") },\r
623   { (COLORREF)0, 0, N_("Normal Text") },\r
624   { (COLORREF)0, 0, N_("None") }\r
625 };\r
626 \r
627 \r
628 \r
629 static char *commentTitle;\r
630 static char *commentText;\r
631 static int commentIndex;\r
632 static Boolean editComment = FALSE;\r
633 \r
634 \r
635 char errorTitle[MSG_SIZ];\r
636 char errorMessage[2*MSG_SIZ];\r
637 HWND errorDialog = NULL;\r
638 BOOLEAN moveErrorMessageUp = FALSE;\r
639 BOOLEAN consoleEcho = TRUE;\r
640 CHARFORMAT consoleCF;\r
641 COLORREF consoleBackgroundColor;\r
642 \r
643 char *programVersion;\r
644 \r
645 #define CPReal 1\r
646 #define CPComm 2\r
647 #define CPSock 3\r
648 #define CPRcmd 4\r
649 typedef int CPKind;\r
650 \r
651 typedef struct {\r
652   CPKind kind;\r
653   HANDLE hProcess;\r
654   DWORD pid;\r
655   HANDLE hTo;\r
656   HANDLE hFrom;\r
657   SOCKET sock;\r
658   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
659 } ChildProc;\r
660 \r
661 #define INPUT_SOURCE_BUF_SIZE 4096\r
662 \r
663 typedef struct _InputSource {\r
664   CPKind kind;\r
665   HANDLE hFile;\r
666   SOCKET sock;\r
667   int lineByLine;\r
668   HANDLE hThread;\r
669   DWORD id;\r
670   char buf[INPUT_SOURCE_BUF_SIZE];\r
671   char *next;\r
672   DWORD count;\r
673   int error;\r
674   InputCallback func;\r
675   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
676   VOIDSTAR closure;\r
677 } InputSource;\r
678 \r
679 InputSource *consoleInputSource;\r
680 \r
681 DCB dcb;\r
682 \r
683 /* forward */\r
684 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
685 VOID ConsoleCreate();\r
686 LRESULT CALLBACK\r
687   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
688 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
689 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
690 VOID ParseCommSettings(char *arg, DCB *dcb);\r
691 LRESULT CALLBACK\r
692   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
693 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
694 void ParseIcsTextMenu(char *icsTextMenuString);\r
695 VOID PopUpNameDialog(char firstchar);\r
696 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
697 \r
698 /* [AS] */\r
699 int NewGameFRC();\r
700 int GameListOptions();\r
701 \r
702 int dummy; // [HGM] for obsolete args\r
703 \r
704 HWND hwndMain = NULL;        /* root window*/\r
705 HWND hwndConsole = NULL;\r
706 HWND commentDialog = NULL;\r
707 HWND moveHistoryDialog = NULL;\r
708 HWND evalGraphDialog = NULL;\r
709 HWND engineOutputDialog = NULL;\r
710 HWND gameListDialog = NULL;\r
711 HWND editTagsDialog = NULL;\r
712 \r
713 int commentUp = FALSE;\r
714 \r
715 WindowPlacement wpMain;\r
716 WindowPlacement wpConsole;\r
717 WindowPlacement wpComment;\r
718 WindowPlacement wpMoveHistory;\r
719 WindowPlacement wpEvalGraph;\r
720 WindowPlacement wpEngineOutput;\r
721 WindowPlacement wpGameList;\r
722 WindowPlacement wpTags;\r
723 \r
724 VOID EngineOptionsPopup(); // [HGM] settings\r
725 \r
726 VOID GothicPopUp(char *title, VariantClass variant);\r
727 /*\r
728  * Setting "frozen" should disable all user input other than deleting\r
729  * the window.  We do this while engines are initializing themselves.\r
730  */\r
731 static int frozen = 0;\r
732 static int oldMenuItemState[MENU_BAR_ITEMS];\r
733 void FreezeUI()\r
734 {\r
735   HMENU hmenu;\r
736   int i;\r
737 \r
738   if (frozen) return;\r
739   frozen = 1;\r
740   hmenu = GetMenu(hwndMain);\r
741   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
742     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
743   }\r
744   DrawMenuBar(hwndMain);\r
745 }\r
746 \r
747 /* Undo a FreezeUI */\r
748 void ThawUI()\r
749 {\r
750   HMENU hmenu;\r
751   int i;\r
752 \r
753   if (!frozen) return;\r
754   frozen = 0;\r
755   hmenu = GetMenu(hwndMain);\r
756   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
757     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
758   }\r
759   DrawMenuBar(hwndMain);\r
760 }\r
761 \r
762 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
763 \r
764 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
765 #ifdef JAWS\r
766 #include "jaws.c"\r
767 #else\r
768 #define JAWS_INIT\r
769 #define JAWS_ARGS\r
770 #define JAWS_ALT_INTERCEPT\r
771 #define JAWS_KBUP_NAVIGATION\r
772 #define JAWS_KBDOWN_NAVIGATION\r
773 #define JAWS_MENU_ITEMS\r
774 #define JAWS_SILENCE\r
775 #define JAWS_REPLAY\r
776 #define JAWS_ACCEL\r
777 #define JAWS_COPYRIGHT\r
778 #define JAWS_DELETE(X) X\r
779 #define SAYMACHINEMOVE()\r
780 #define SAY(X)\r
781 #endif\r
782 \r
783 /*---------------------------------------------------------------------------*\\r
784  *\r
785  * WinMain\r
786  *\r
787 \*---------------------------------------------------------------------------*/\r
788 \r
789 int APIENTRY\r
790 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
791         LPSTR lpCmdLine, int nCmdShow)\r
792 {\r
793   MSG msg;\r
794   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
795 //  INITCOMMONCONTROLSEX ex;\r
796 \r
797   debugFP = stderr;\r
798 \r
799   LoadLibrary("RICHED32.DLL");\r
800   consoleCF.cbSize = sizeof(CHARFORMAT);\r
801 \r
802   if (!InitApplication(hInstance)) {\r
803     return (FALSE);\r
804   }\r
805   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
806     return (FALSE);\r
807   }\r
808 \r
809   JAWS_INIT\r
810   TranslateMenus(1);\r
811 \r
812 //  InitCommonControlsEx(&ex);\r
813   InitCommonControls();\r
814 \r
815   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
816   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
817   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
818 \r
819   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
820 \r
821   while (GetMessage(&msg, /* message structure */\r
822                     NULL, /* handle of window receiving the message */\r
823                     0,    /* lowest message to examine */\r
824                     0))   /* highest message to examine */\r
825     {\r
826 \r
827       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
828         // [HGM] navigate: switch between all windows with tab\r
829         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
830         int i, currentElement = 0;\r
831 \r
832         // first determine what element of the chain we come from (if any)\r
833         if(appData.icsActive) {\r
834             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
835             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
836         }\r
837         if(engineOutputDialog && EngineOutputIsUp()) {\r
838             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
839             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
840         }\r
841         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
842             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
843         }\r
844         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
845         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
846         if(msg.hwnd == e1)                 currentElement = 2; else\r
847         if(msg.hwnd == e2)                 currentElement = 3; else\r
848         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
849         if(msg.hwnd == mh)                currentElement = 4; else\r
850         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
851         if(msg.hwnd == hText)  currentElement = 5; else\r
852         if(msg.hwnd == hInput) currentElement = 6; else\r
853         for (i = 0; i < N_BUTTONS; i++) {\r
854             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
855         }\r
856 \r
857         // determine where to go to\r
858         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
859           do {\r
860             currentElement = (currentElement + direction) % 7;\r
861             switch(currentElement) {\r
862                 case 0:\r
863                   h = hwndMain; break; // passing this case always makes the loop exit\r
864                 case 1:\r
865                   h = buttonDesc[0].hwnd; break; // could be NULL\r
866                 case 2:\r
867                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
868                   h = e1; break;\r
869                 case 3:\r
870                   if(!EngineOutputIsUp()) continue;\r
871                   h = e2; break;\r
872                 case 4:\r
873                   if(!MoveHistoryIsUp()) continue;\r
874                   h = mh; break;\r
875 //              case 6: // input to eval graph does not seem to get here!\r
876 //                if(!EvalGraphIsUp()) continue;\r
877 //                h = evalGraphDialog; break;\r
878                 case 5:\r
879                   if(!appData.icsActive) continue;\r
880                   SAY("display");\r
881                   h = hText; break;\r
882                 case 6:\r
883                   if(!appData.icsActive) continue;\r
884                   SAY("input");\r
885                   h = hInput; break;\r
886             }\r
887           } while(h == 0);\r
888 \r
889           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
890           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
891           SetFocus(h);\r
892 \r
893           continue; // this message now has been processed\r
894         }\r
895       }\r
896 \r
897       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
898           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
899           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
900           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
901           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
902           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
903           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
904           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
905           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
906           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
907         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
908         for(i=0; i<MAX_CHAT; i++) \r
909             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
910                 done = 1; break;\r
911         }\r
912         if(done) continue; // [HGM] chat: end patch\r
913         TranslateMessage(&msg); /* Translates virtual key codes */\r
914         DispatchMessage(&msg);  /* Dispatches message to window */\r
915       }\r
916     }\r
917 \r
918 \r
919   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
920 }\r
921 \r
922 /*---------------------------------------------------------------------------*\\r
923  *\r
924  * Initialization functions\r
925  *\r
926 \*---------------------------------------------------------------------------*/\r
927 \r
928 void\r
929 SetUserLogo()\r
930 {   // update user logo if necessary\r
931     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
932 \r
933     if(appData.autoLogo) {\r
934           curName = UserName();\r
935           if(strcmp(curName, oldUserName)) {\r
936                 GetCurrentDirectory(MSG_SIZ, dir);\r
937                 SetCurrentDirectory(installDir);\r
938                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
939                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
940                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
941                 if(userLogo == NULL)\r
942                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
943                 SetCurrentDirectory(dir); /* return to prev directory */\r
944           }\r
945     }\r
946 }\r
947 \r
948 BOOL\r
949 InitApplication(HINSTANCE hInstance)\r
950 {\r
951   WNDCLASS wc;\r
952 \r
953   /* Fill in window class structure with parameters that describe the */\r
954   /* main window. */\r
955 \r
956   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
957   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
958   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
959   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
960   wc.hInstance     = hInstance;         /* Owner of this class */\r
961   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
962   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
963   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
964   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
965   wc.lpszClassName = szAppName;                 /* Name to register as */\r
966 \r
967   /* Register the window class and return success/failure code. */\r
968   if (!RegisterClass(&wc)) return FALSE;\r
969 \r
970   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
971   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
972   wc.cbClsExtra    = 0;\r
973   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
974   wc.hInstance     = hInstance;\r
975   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
976   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
977   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
978   wc.lpszMenuName  = NULL;\r
979   wc.lpszClassName = szConsoleName;\r
980 \r
981   if (!RegisterClass(&wc)) return FALSE;\r
982   return TRUE;\r
983 }\r
984 \r
985 \r
986 /* Set by InitInstance, used by EnsureOnScreen */\r
987 int screenHeight, screenWidth;\r
988 RECT screenGeometry;\r
989 \r
990 void\r
991 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
992 {\r
993 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
994   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
995   if (*x > screenGeometry.right - 32) *x = screenGeometry.left;\r
996   if (*y > screenGeometry.bottom - 32) *y = screenGeometry.top;\r
997   if (*x < screenGeometry.left + minX) *x = screenGeometry.left + minX;\r
998   if (*y < screenGeometry.top + minY) *y = screenGeometry.top + minY;\r
999 }\r
1000 \r
1001 VOID\r
1002 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
1003 {\r
1004   char buf[MSG_SIZ], dir[MSG_SIZ];\r
1005   GetCurrentDirectory(MSG_SIZ, dir);\r
1006   SetCurrentDirectory(installDir);\r
1007   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
1008       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1009 \r
1010       if (cps->programLogo == NULL && appData.debugMode) {\r
1011           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1012       }\r
1013   } else if(appData.autoLogo) {\r
1014       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1015         char *opponent = "";\r
1016         if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;\r
1017         if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;\r
1018         sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);\r
1019         if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {\r
1020             sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1021             cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1022         }\r
1023       } else\r
1024       if(appData.directory[n] && appData.directory[n][0]) {\r
1025         SetCurrentDirectory(appData.directory[n]);\r
1026         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1027       }\r
1028   }\r
1029   SetCurrentDirectory(dir); /* return to prev directory */\r
1030 }\r
1031 \r
1032 VOID\r
1033 InitTextures()\r
1034 {\r
1035   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1036   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1037   \r
1038   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1039       if(liteBackTexture) DeleteObject(liteBackTexture);\r
1040       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1041       liteBackTextureMode = appData.liteBackTextureMode;\r
1042 \r
1043       if (liteBackTexture == NULL && appData.debugMode) {\r
1044           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1045       }\r
1046   }\r
1047   \r
1048   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1049       if(darkBackTexture) DeleteObject(darkBackTexture);\r
1050       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1051       darkBackTextureMode = appData.darkBackTextureMode;\r
1052 \r
1053       if (darkBackTexture == NULL && appData.debugMode) {\r
1054           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1055       }\r
1056   }\r
1057 }\r
1058 \r
1059 #ifndef SM_CXVIRTUALSCREEN\r
1060 #define SM_CXVIRTUALSCREEN 78\r
1061 #endif\r
1062 #ifndef SM_CYVIRTUALSCREEN\r
1063 #define SM_CYVIRTUALSCREEN 79\r
1064 #endif\r
1065 #ifndef SM_XVIRTUALSCREEN \r
1066 #define SM_XVIRTUALSCREEN 76\r
1067 #endif\r
1068 #ifndef SM_YVIRTUALSCREEN \r
1069 #define SM_YVIRTUALSCREEN 77\r
1070 #endif\r
1071 \r
1072 VOID\r
1073 InitGeometry()\r
1074 {\r
1075   screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);\r
1076   if( !screenHeight ) screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1077   screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);\r
1078   if( !screenWidth ) screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1079   screenGeometry.left = GetSystemMetrics(SM_XVIRTUALSCREEN);\r
1080   screenGeometry.top = GetSystemMetrics(SM_YVIRTUALSCREEN);\r
1081   screenGeometry.right = screenGeometry.left + screenWidth;\r
1082   screenGeometry.bottom = screenGeometry.top + screenHeight;\r
1083 }\r
1084 \r
1085 BOOL\r
1086 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1087 {\r
1088   HWND hwnd; /* Main window handle. */\r
1089   int ibs;\r
1090   WINDOWPLACEMENT wp;\r
1091   char *filepart;\r
1092 \r
1093   hInst = hInstance;    /* Store instance handle in our global variable */\r
1094   programName = szAppName;\r
1095 \r
1096   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1097     *filepart = NULLCHAR;\r
1098     SetCurrentDirectory(installDir);\r
1099   } else {\r
1100     GetCurrentDirectory(MSG_SIZ, installDir);\r
1101   }\r
1102   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1103   InitGeometry();\r
1104   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1105   /* xboard, and older WinBoards, controlled the move sound with the\r
1106      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1107      always turn the option on (so that the backend will call us),\r
1108      then let the user turn the sound off by setting it to silence if\r
1109      desired.  To accommodate old winboard.ini files saved by old\r
1110      versions of WinBoard, we also turn off the sound if the option\r
1111      was initially set to false. [HGM] taken out of InitAppData */\r
1112   if (!appData.ringBellAfterMoves) {\r
1113     sounds[(int)SoundMove].name = strdup("");\r
1114     appData.ringBellAfterMoves = TRUE;\r
1115   }\r
1116   if (appData.debugMode) {\r
1117     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1118     setbuf(debugFP, NULL);\r
1119   }\r
1120 \r
1121   LoadLanguageFile(appData.language);\r
1122 \r
1123   InitBackEnd1();\r
1124 \r
1125 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1126 //  InitEngineUCI( installDir, &second );\r
1127 \r
1128   /* Create a main window for this application instance. */\r
1129   hwnd = CreateWindow(szAppName, szTitle,\r
1130                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1131                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1132                       NULL, NULL, hInstance, NULL);\r
1133   hwndMain = hwnd;\r
1134 \r
1135   /* If window could not be created, return "failure" */\r
1136   if (!hwnd) {\r
1137     return (FALSE);\r
1138   }\r
1139 \r
1140   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1141   LoadLogo(&first, 0, FALSE);\r
1142   LoadLogo(&second, 1, appData.icsActive);\r
1143 \r
1144   SetUserLogo();\r
1145 \r
1146   iconWhite = LoadIcon(hInstance, "icon_white");\r
1147   iconBlack = LoadIcon(hInstance, "icon_black");\r
1148   iconCurrent = iconWhite;\r
1149   InitDrawingColors();\r
1150 \r
1151   InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args\r
1152   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1153     /* Compute window size for each board size, and use the largest\r
1154        size that fits on this screen as the default. */\r
1155     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1156     if (boardSize == (BoardSize)-1 &&\r
1157         winH <= screenHeight\r
1158            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1159         && winW <= screenWidth) {\r
1160       boardSize = (BoardSize)ibs;\r
1161     }\r
1162   }\r
1163 \r
1164   InitDrawingSizes(boardSize, 0);\r
1165   RecentEngineMenu(appData.recentEngineList);\r
1166   InitMenuChecks();\r
1167   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1168 \r
1169   /* [AS] Load textures if specified */\r
1170   InitTextures();\r
1171 \r
1172   mysrandom( (unsigned) time(NULL) );\r
1173 \r
1174   /* [AS] Restore layout */\r
1175   if( wpMoveHistory.visible ) {\r
1176       MoveHistoryPopUp();\r
1177   }\r
1178 \r
1179   if( wpEvalGraph.visible ) {\r
1180       EvalGraphPopUp();\r
1181   }\r
1182 \r
1183   if( wpEngineOutput.visible ) {\r
1184       EngineOutputPopUp();\r
1185   }\r
1186 \r
1187   /* Make the window visible; update its client area; and return "success" */\r
1188   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1189   wp.length = sizeof(WINDOWPLACEMENT);\r
1190   wp.flags = 0;\r
1191   wp.showCmd = nCmdShow;\r
1192   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1193   wp.rcNormalPosition.left = wpMain.x;\r
1194   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1195   wp.rcNormalPosition.top = wpMain.y;\r
1196   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1197   SetWindowPlacement(hwndMain, &wp);\r
1198 \r
1199   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1200 \r
1201   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1202                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1203 \r
1204   if (hwndConsole) {\r
1205 #if AOT_CONSOLE\r
1206     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1207                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1208 #endif\r
1209     ShowWindow(hwndConsole, nCmdShow);\r
1210     SetActiveWindow(hwndConsole);\r
1211   }\r
1212   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1213   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1214 \r
1215   return TRUE;\r
1216 \r
1217 }\r
1218 \r
1219 VOID\r
1220 InitMenuChecks()\r
1221 {\r
1222   HMENU hmenu = GetMenu(hwndMain);\r
1223 \r
1224   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1225                         MF_BYCOMMAND|((appData.icsActive &&\r
1226                                        *appData.icsCommPort != NULLCHAR) ?\r
1227                                       MF_ENABLED : MF_GRAYED));\r
1228   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1229                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1230                                      MF_CHECKED : MF_UNCHECKED));\r
1231 }\r
1232 \r
1233 //---------------------------------------------------------------------------------------------------------\r
1234 \r
1235 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1236 #define XBOARD FALSE\r
1237 \r
1238 #define OPTCHAR "/"\r
1239 #define SEPCHAR "="\r
1240 #define TOPLEVEL 0\r
1241 \r
1242 #include "args.h"\r
1243 \r
1244 // front-end part of option handling\r
1245 \r
1246 VOID\r
1247 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1248 {\r
1249   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1250   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1251   DeleteDC(hdc);\r
1252   lf->lfWidth = 0;\r
1253   lf->lfEscapement = 0;\r
1254   lf->lfOrientation = 0;\r
1255   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1256   lf->lfItalic = mfp->italic;\r
1257   lf->lfUnderline = mfp->underline;\r
1258   lf->lfStrikeOut = mfp->strikeout;\r
1259   lf->lfCharSet = mfp->charset;\r
1260   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1261   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1262   lf->lfQuality = DEFAULT_QUALITY;\r
1263   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1264     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1265 }\r
1266 \r
1267 void\r
1268 CreateFontInMF(MyFont *mf)\r
1269\r
1270   LFfromMFP(&mf->lf, &mf->mfp);\r
1271   if (mf->hf) DeleteObject(mf->hf);\r
1272   mf->hf = CreateFontIndirect(&mf->lf);\r
1273 }\r
1274 \r
1275 // [HGM] This platform-dependent table provides the location for storing the color info\r
1276 void *\r
1277 colorVariable[] = {\r
1278   &whitePieceColor, \r
1279   &blackPieceColor, \r
1280   &lightSquareColor,\r
1281   &darkSquareColor, \r
1282   &highlightSquareColor,\r
1283   &premoveHighlightColor,\r
1284   NULL,\r
1285   &consoleBackgroundColor,\r
1286   &appData.fontForeColorWhite,\r
1287   &appData.fontBackColorWhite,\r
1288   &appData.fontForeColorBlack,\r
1289   &appData.fontBackColorBlack,\r
1290   &appData.evalHistColorWhite,\r
1291   &appData.evalHistColorBlack,\r
1292   &appData.highlightArrowColor,\r
1293 };\r
1294 \r
1295 /* Command line font name parser.  NULL name means do nothing.\r
1296    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1297    For backward compatibility, syntax without the colon is also\r
1298    accepted, but font names with digits in them won't work in that case.\r
1299 */\r
1300 VOID\r
1301 ParseFontName(char *name, MyFontParams *mfp)\r
1302 {\r
1303   char *p, *q;\r
1304   if (name == NULL) return;\r
1305   p = name;\r
1306   q = strchr(p, ':');\r
1307   if (q) {\r
1308     if (q - p >= sizeof(mfp->faceName))\r
1309       ExitArgError(_("Font name too long:"), name, TRUE);\r
1310     memcpy(mfp->faceName, p, q - p);\r
1311     mfp->faceName[q - p] = NULLCHAR;\r
1312     p = q + 1;\r
1313   } else {\r
1314     q = mfp->faceName;\r
1315 \r
1316     while (*p && !isdigit(*p)) {\r
1317       *q++ = *p++;\r
1318       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1319         ExitArgError(_("Font name too long:"), name, TRUE);\r
1320     }\r
1321     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1322     *q = NULLCHAR;\r
1323   }\r
1324   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1325   mfp->pointSize = (float) atof(p);\r
1326   mfp->bold = (strchr(p, 'b') != NULL);\r
1327   mfp->italic = (strchr(p, 'i') != NULL);\r
1328   mfp->underline = (strchr(p, 'u') != NULL);\r
1329   mfp->strikeout = (strchr(p, 's') != NULL);\r
1330   mfp->charset = DEFAULT_CHARSET;\r
1331   q = strchr(p, 'c');\r
1332   if (q)\r
1333     mfp->charset = (BYTE) atoi(q+1);\r
1334 }\r
1335 \r
1336 void\r
1337 ParseFont(char *name, int number)\r
1338 { // wrapper to shield back-end from 'font'\r
1339   ParseFontName(name, &font[boardSize][number]->mfp);\r
1340 }\r
1341 \r
1342 void\r
1343 SetFontDefaults()\r
1344 { // in WB  we have a 2D array of fonts; this initializes their description\r
1345   int i, j;\r
1346   /* Point font array elements to structures and\r
1347      parse default font names */\r
1348   for (i=0; i<NUM_FONTS; i++) {\r
1349     for (j=0; j<NUM_SIZES; j++) {\r
1350       font[j][i] = &fontRec[j][i];\r
1351       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1352     }\r
1353   }\r
1354 }\r
1355 \r
1356 void\r
1357 CreateFonts()\r
1358 { // here we create the actual fonts from the selected descriptions\r
1359   int i, j;\r
1360   for (i=0; i<NUM_FONTS; i++) {\r
1361     for (j=0; j<NUM_SIZES; j++) {\r
1362       CreateFontInMF(font[j][i]);\r
1363     }\r
1364   }\r
1365 }\r
1366 /* Color name parser.\r
1367    X version accepts X color names, but this one\r
1368    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1369 COLORREF\r
1370 ParseColorName(char *name)\r
1371 {\r
1372   int red, green, blue, count;\r
1373   char buf[MSG_SIZ];\r
1374 \r
1375   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1376   if (count != 3) {\r
1377     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1378       &red, &green, &blue);\r
1379   }\r
1380   if (count != 3) {\r
1381     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1382     DisplayError(buf, 0);\r
1383     return RGB(0, 0, 0);\r
1384   }\r
1385   return PALETTERGB(red, green, blue);\r
1386 }\r
1387 \r
1388 void\r
1389 ParseColor(int n, char *name)\r
1390 { // for WinBoard the color is an int, which needs to be derived from the string\r
1391   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1392 }\r
1393 \r
1394 void\r
1395 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1396 {\r
1397   char *e = argValue;\r
1398   int eff = 0;\r
1399 \r
1400   while (*e) {\r
1401     if (*e == 'b')      eff |= CFE_BOLD;\r
1402     else if (*e == 'i') eff |= CFE_ITALIC;\r
1403     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1404     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1405     else if (*e == '#' || isdigit(*e)) break;\r
1406     e++;\r
1407   }\r
1408   *effects = eff;\r
1409   *color   = ParseColorName(e);\r
1410 }\r
1411 \r
1412 void\r
1413 ParseTextAttribs(ColorClass cc, char *s)\r
1414 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1415     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1416     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1417 }\r
1418 \r
1419 void\r
1420 ParseBoardSize(void *addr, char *name)\r
1421 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1422   BoardSize bs = SizeTiny;\r
1423   while (sizeInfo[bs].name != NULL) {\r
1424     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1425         *(BoardSize *)addr = bs;\r
1426         return;\r
1427     }\r
1428     bs++;\r
1429   }\r
1430   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1431 }\r
1432 \r
1433 void\r
1434 LoadAllSounds()\r
1435 { // [HGM] import name from appData first\r
1436   ColorClass cc;\r
1437   SoundClass sc;\r
1438   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1439     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1440     textAttribs[cc].sound.data = NULL;\r
1441     MyLoadSound(&textAttribs[cc].sound);\r
1442   }\r
1443   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1444     textAttribs[cc].sound.name = strdup("");\r
1445     textAttribs[cc].sound.data = NULL;\r
1446   }\r
1447   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1448     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1449     sounds[sc].data = NULL;\r
1450     MyLoadSound(&sounds[sc]);\r
1451   }\r
1452 }\r
1453 \r
1454 void\r
1455 SetCommPortDefaults()\r
1456 {\r
1457    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1458   dcb.DCBlength = sizeof(DCB);\r
1459   dcb.BaudRate = 9600;\r
1460   dcb.fBinary = TRUE;\r
1461   dcb.fParity = FALSE;\r
1462   dcb.fOutxCtsFlow = FALSE;\r
1463   dcb.fOutxDsrFlow = FALSE;\r
1464   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1465   dcb.fDsrSensitivity = FALSE;\r
1466   dcb.fTXContinueOnXoff = TRUE;\r
1467   dcb.fOutX = FALSE;\r
1468   dcb.fInX = FALSE;\r
1469   dcb.fNull = FALSE;\r
1470   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1471   dcb.fAbortOnError = FALSE;\r
1472   dcb.ByteSize = 7;\r
1473   dcb.Parity = SPACEPARITY;\r
1474   dcb.StopBits = ONESTOPBIT;\r
1475 }\r
1476 \r
1477 // [HGM] args: these three cases taken out to stay in front-end\r
1478 void\r
1479 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1480 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1481         // while the curent board size determines the element. This system should be ported to XBoard.\r
1482         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1483         int bs;\r
1484         for (bs=0; bs<NUM_SIZES; bs++) {\r
1485           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1486           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1487           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1488             ad->argName, mfp->faceName, mfp->pointSize,\r
1489             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1490             mfp->bold ? "b" : "",\r
1491             mfp->italic ? "i" : "",\r
1492             mfp->underline ? "u" : "",\r
1493             mfp->strikeout ? "s" : "",\r
1494             (int)mfp->charset);\r
1495         }\r
1496       }\r
1497 \r
1498 void\r
1499 ExportSounds()\r
1500 { // [HGM] copy the names from the internal WB variables to appData\r
1501   ColorClass cc;\r
1502   SoundClass sc;\r
1503   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1504     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1505   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1506     (&appData.soundMove)[sc] = sounds[sc].name;\r
1507 }\r
1508 \r
1509 void\r
1510 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1511 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1512         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1513         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1514           (ta->effects & CFE_BOLD) ? "b" : "",\r
1515           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1516           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1517           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1518           (ta->effects) ? " " : "",\r
1519           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1520       }\r
1521 \r
1522 void\r
1523 SaveColor(FILE *f, ArgDescriptor *ad)\r
1524 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1525         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1526         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1527           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1528 }\r
1529 \r
1530 void\r
1531 SaveBoardSize(FILE *f, char *name, void *addr)\r
1532 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1533   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1534 }\r
1535 \r
1536 void\r
1537 ParseCommPortSettings(char *s)\r
1538 { // wrapper to keep dcb from back-end\r
1539   ParseCommSettings(s, &dcb);\r
1540 }\r
1541 \r
1542 void\r
1543 GetWindowCoords()\r
1544 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1545   GetActualPlacement(hwndMain, &wpMain);\r
1546   GetActualPlacement(hwndConsole, &wpConsole);\r
1547   GetActualPlacement(commentDialog, &wpComment);\r
1548   GetActualPlacement(editTagsDialog, &wpTags);\r
1549   GetActualPlacement(gameListDialog, &wpGameList);\r
1550   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1551   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1552   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1553 }\r
1554 \r
1555 void\r
1556 PrintCommPortSettings(FILE *f, char *name)\r
1557 { // wrapper to shield back-end from DCB\r
1558       PrintCommSettings(f, name, &dcb);\r
1559 }\r
1560 \r
1561 int\r
1562 MySearchPath(char *installDir, char *name, char *fullname)\r
1563 {\r
1564   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1565   if(name[0]== '%') {\r
1566     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1567     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1568       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1569       *strchr(buf, '%') = 0;\r
1570       strcat(fullname, getenv(buf));\r
1571       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1572     }\r
1573     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1574     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1575     return (int) strlen(fullname);\r
1576   }\r
1577   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1578 }\r
1579 \r
1580 int\r
1581 MyGetFullPathName(char *name, char *fullname)\r
1582 {\r
1583   char *dummy;\r
1584   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1585 }\r
1586 \r
1587 int\r
1588 MainWindowUp()\r
1589 { // [HGM] args: allows testing if main window is realized from back-end\r
1590   return hwndMain != NULL;\r
1591 }\r
1592 \r
1593 void\r
1594 PopUpStartupDialog()\r
1595 {\r
1596     FARPROC lpProc;\r
1597     \r
1598     LoadLanguageFile(appData.language);\r
1599     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1600     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1601     FreeProcInstance(lpProc);\r
1602 }\r
1603 \r
1604 /*---------------------------------------------------------------------------*\\r
1605  *\r
1606  * GDI board drawing routines\r
1607  *\r
1608 \*---------------------------------------------------------------------------*/\r
1609 \r
1610 /* [AS] Draw square using background texture */\r
1611 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1612 {\r
1613     XFORM   x;\r
1614 \r
1615     if( mode == 0 ) {\r
1616         return; /* Should never happen! */\r
1617     }\r
1618 \r
1619     SetGraphicsMode( dst, GM_ADVANCED );\r
1620 \r
1621     switch( mode ) {\r
1622     case 1:\r
1623         /* Identity */\r
1624         break;\r
1625     case 2:\r
1626         /* X reflection */\r
1627         x.eM11 = -1.0;\r
1628         x.eM12 = 0;\r
1629         x.eM21 = 0;\r
1630         x.eM22 = 1.0;\r
1631         x.eDx = (FLOAT) dw + dx - 1;\r
1632         x.eDy = 0;\r
1633         dx = 0;\r
1634         SetWorldTransform( dst, &x );\r
1635         break;\r
1636     case 3:\r
1637         /* Y reflection */\r
1638         x.eM11 = 1.0;\r
1639         x.eM12 = 0;\r
1640         x.eM21 = 0;\r
1641         x.eM22 = -1.0;\r
1642         x.eDx = 0;\r
1643         x.eDy = (FLOAT) dh + dy - 1;\r
1644         dy = 0;\r
1645         SetWorldTransform( dst, &x );\r
1646         break;\r
1647     case 4:\r
1648         /* X/Y flip */\r
1649         x.eM11 = 0;\r
1650         x.eM12 = 1.0;\r
1651         x.eM21 = 1.0;\r
1652         x.eM22 = 0;\r
1653         x.eDx = (FLOAT) dx;\r
1654         x.eDy = (FLOAT) dy;\r
1655         dx = 0;\r
1656         dy = 0;\r
1657         SetWorldTransform( dst, &x );\r
1658         break;\r
1659     }\r
1660 \r
1661     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1662 \r
1663     x.eM11 = 1.0;\r
1664     x.eM12 = 0;\r
1665     x.eM21 = 0;\r
1666     x.eM22 = 1.0;\r
1667     x.eDx = 0;\r
1668     x.eDy = 0;\r
1669     SetWorldTransform( dst, &x );\r
1670 \r
1671     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1672 }\r
1673 \r
1674 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1675 enum {\r
1676     PM_WP = (int) WhitePawn, \r
1677     PM_WN = (int) WhiteKnight, \r
1678     PM_WB = (int) WhiteBishop, \r
1679     PM_WR = (int) WhiteRook, \r
1680     PM_WQ = (int) WhiteQueen, \r
1681     PM_WF = (int) WhiteFerz, \r
1682     PM_WW = (int) WhiteWazir, \r
1683     PM_WE = (int) WhiteAlfil, \r
1684     PM_WM = (int) WhiteMan, \r
1685     PM_WO = (int) WhiteCannon, \r
1686     PM_WU = (int) WhiteUnicorn, \r
1687     PM_WH = (int) WhiteNightrider, \r
1688     PM_WA = (int) WhiteAngel, \r
1689     PM_WC = (int) WhiteMarshall, \r
1690     PM_WAB = (int) WhiteCardinal, \r
1691     PM_WD = (int) WhiteDragon, \r
1692     PM_WL = (int) WhiteLance, \r
1693     PM_WS = (int) WhiteCobra, \r
1694     PM_WV = (int) WhiteFalcon, \r
1695     PM_WSG = (int) WhiteSilver, \r
1696     PM_WG = (int) WhiteGrasshopper, \r
1697     PM_WK = (int) WhiteKing,\r
1698     PM_BP = (int) BlackPawn, \r
1699     PM_BN = (int) BlackKnight, \r
1700     PM_BB = (int) BlackBishop, \r
1701     PM_BR = (int) BlackRook, \r
1702     PM_BQ = (int) BlackQueen, \r
1703     PM_BF = (int) BlackFerz, \r
1704     PM_BW = (int) BlackWazir, \r
1705     PM_BE = (int) BlackAlfil, \r
1706     PM_BM = (int) BlackMan,\r
1707     PM_BO = (int) BlackCannon, \r
1708     PM_BU = (int) BlackUnicorn, \r
1709     PM_BH = (int) BlackNightrider, \r
1710     PM_BA = (int) BlackAngel, \r
1711     PM_BC = (int) BlackMarshall, \r
1712     PM_BG = (int) BlackGrasshopper, \r
1713     PM_BAB = (int) BlackCardinal,\r
1714     PM_BD = (int) BlackDragon,\r
1715     PM_BL = (int) BlackLance,\r
1716     PM_BS = (int) BlackCobra,\r
1717     PM_BV = (int) BlackFalcon,\r
1718     PM_BSG = (int) BlackSilver,\r
1719     PM_BK = (int) BlackKing\r
1720 };\r
1721 \r
1722 static HFONT hPieceFont = NULL;\r
1723 static HBITMAP hPieceMask[(int) EmptySquare];\r
1724 static HBITMAP hPieceFace[(int) EmptySquare];\r
1725 static int fontBitmapSquareSize = 0;\r
1726 static char pieceToFontChar[(int) EmptySquare] =\r
1727                               { 'p', 'n', 'b', 'r', 'q', \r
1728                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1729                       'k', 'o', 'm', 'v', 't', 'w', \r
1730                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1731                                                               'l' };\r
1732 \r
1733 extern BOOL SetCharTable( char *table, const char * map );\r
1734 /* [HGM] moved to backend.c */\r
1735 \r
1736 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1737 {\r
1738     HBRUSH hbrush;\r
1739     BYTE r1 = GetRValue( color );\r
1740     BYTE g1 = GetGValue( color );\r
1741     BYTE b1 = GetBValue( color );\r
1742     BYTE r2 = r1 / 2;\r
1743     BYTE g2 = g1 / 2;\r
1744     BYTE b2 = b1 / 2;\r
1745     RECT rc;\r
1746 \r
1747     /* Create a uniform background first */\r
1748     hbrush = CreateSolidBrush( color );\r
1749     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1750     FillRect( hdc, &rc, hbrush );\r
1751     DeleteObject( hbrush );\r
1752     \r
1753     if( mode == 1 ) {\r
1754         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1755         int steps = squareSize / 2;\r
1756         int i;\r
1757 \r
1758         for( i=0; i<steps; i++ ) {\r
1759             BYTE r = r1 - (r1-r2) * i / steps;\r
1760             BYTE g = g1 - (g1-g2) * i / steps;\r
1761             BYTE b = b1 - (b1-b2) * i / steps;\r
1762 \r
1763             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1764             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1765             FillRect( hdc, &rc, hbrush );\r
1766             DeleteObject(hbrush);\r
1767         }\r
1768     }\r
1769     else if( mode == 2 ) {\r
1770         /* Diagonal gradient, good more or less for every piece */\r
1771         POINT triangle[3];\r
1772         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1773         HBRUSH hbrush_old;\r
1774         int steps = squareSize;\r
1775         int i;\r
1776 \r
1777         triangle[0].x = squareSize - steps;\r
1778         triangle[0].y = squareSize;\r
1779         triangle[1].x = squareSize;\r
1780         triangle[1].y = squareSize;\r
1781         triangle[2].x = squareSize;\r
1782         triangle[2].y = squareSize - steps;\r
1783 \r
1784         for( i=0; i<steps; i++ ) {\r
1785             BYTE r = r1 - (r1-r2) * i / steps;\r
1786             BYTE g = g1 - (g1-g2) * i / steps;\r
1787             BYTE b = b1 - (b1-b2) * i / steps;\r
1788 \r
1789             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1790             hbrush_old = SelectObject( hdc, hbrush );\r
1791             Polygon( hdc, triangle, 3 );\r
1792             SelectObject( hdc, hbrush_old );\r
1793             DeleteObject(hbrush);\r
1794             triangle[0].x++;\r
1795             triangle[2].y++;\r
1796         }\r
1797 \r
1798         SelectObject( hdc, hpen );\r
1799     }\r
1800 }\r
1801 \r
1802 /*\r
1803     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1804     seems to work ok. The main problem here is to find the "inside" of a chess\r
1805     piece: follow the steps as explained below.\r
1806 */\r
1807 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1808 {\r
1809     HBITMAP hbm;\r
1810     HBITMAP hbm_old;\r
1811     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1812     RECT rc;\r
1813     SIZE sz;\r
1814 \r
1815 \r
1816     POINT pt;\r
1817     int backColor = whitePieceColor; \r
1818     int foreColor = blackPieceColor;\r
1819     \r
1820     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1821         backColor = appData.fontBackColorWhite;\r
1822         foreColor = appData.fontForeColorWhite;\r
1823     }\r
1824     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1825         backColor = appData.fontBackColorBlack;\r
1826         foreColor = appData.fontForeColorBlack;\r
1827     }\r
1828 \r
1829     /* Mask */\r
1830     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1831 \r
1832     hbm_old = SelectObject( hdc, hbm );\r
1833 \r
1834     rc.left = 0;\r
1835     rc.top = 0;\r
1836     rc.right = squareSize;\r
1837     rc.bottom = squareSize;\r
1838 \r
1839     /* Step 1: background is now black */\r
1840     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1841 \r
1842     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1843 \r
1844     pt.x = (squareSize - sz.cx) / 2;\r
1845     pt.y = (squareSize - sz.cy) / 2;\r
1846 \r
1847     SetBkMode( hdc, TRANSPARENT );\r
1848     SetTextColor( hdc, chroma );\r
1849     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1850     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1851 \r
1852     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1853     /* Step 3: the area outside the piece is filled with white */\r
1854 //    FloodFill( hdc, 0, 0, chroma );\r
1855     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1856     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1857     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1858     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1859     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1860     /* \r
1861         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1862         but if the start point is not inside the piece we're lost!\r
1863         There should be a better way to do this... if we could create a region or path\r
1864         from the fill operation we would be fine for example.\r
1865     */\r
1866 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1867     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1868 \r
1869     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1870         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1871         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1872 \r
1873         SelectObject( dc2, bm2 );\r
1874         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1875         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1876         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1877         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1878         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1879 \r
1880         DeleteDC( dc2 );\r
1881         DeleteObject( bm2 );\r
1882     }\r
1883 \r
1884     SetTextColor( hdc, 0 );\r
1885     /* \r
1886         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1887         draw the piece again in black for safety.\r
1888     */\r
1889     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1890 \r
1891     SelectObject( hdc, hbm_old );\r
1892 \r
1893     if( hPieceMask[index] != NULL ) {\r
1894         DeleteObject( hPieceMask[index] );\r
1895     }\r
1896 \r
1897     hPieceMask[index] = hbm;\r
1898 \r
1899     /* Face */\r
1900     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1901 \r
1902     SelectObject( hdc, hbm );\r
1903 \r
1904     {\r
1905         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1906         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1907         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1908 \r
1909         SelectObject( dc1, hPieceMask[index] );\r
1910         SelectObject( dc2, bm2 );\r
1911         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1912         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1913         \r
1914         /* \r
1915             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1916             the piece background and deletes (makes transparent) the rest.\r
1917             Thanks to that mask, we are free to paint the background with the greates\r
1918             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1919             We use this, to make gradients and give the pieces a "roundish" look.\r
1920         */\r
1921         SetPieceBackground( hdc, backColor, 2 );\r
1922         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1923 \r
1924         DeleteDC( dc2 );\r
1925         DeleteDC( dc1 );\r
1926         DeleteObject( bm2 );\r
1927     }\r
1928 \r
1929     SetTextColor( hdc, foreColor );\r
1930     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1931 \r
1932     SelectObject( hdc, hbm_old );\r
1933 \r
1934     if( hPieceFace[index] != NULL ) {\r
1935         DeleteObject( hPieceFace[index] );\r
1936     }\r
1937 \r
1938     hPieceFace[index] = hbm;\r
1939 }\r
1940 \r
1941 static int TranslatePieceToFontPiece( int piece )\r
1942 {\r
1943     switch( piece ) {\r
1944     case BlackPawn:\r
1945         return PM_BP;\r
1946     case BlackKnight:\r
1947         return PM_BN;\r
1948     case BlackBishop:\r
1949         return PM_BB;\r
1950     case BlackRook:\r
1951         return PM_BR;\r
1952     case BlackQueen:\r
1953         return PM_BQ;\r
1954     case BlackKing:\r
1955         return PM_BK;\r
1956     case WhitePawn:\r
1957         return PM_WP;\r
1958     case WhiteKnight:\r
1959         return PM_WN;\r
1960     case WhiteBishop:\r
1961         return PM_WB;\r
1962     case WhiteRook:\r
1963         return PM_WR;\r
1964     case WhiteQueen:\r
1965         return PM_WQ;\r
1966     case WhiteKing:\r
1967         return PM_WK;\r
1968 \r
1969     case BlackAngel:\r
1970         return PM_BA;\r
1971     case BlackMarshall:\r
1972         return PM_BC;\r
1973     case BlackFerz:\r
1974         return PM_BF;\r
1975     case BlackNightrider:\r
1976         return PM_BH;\r
1977     case BlackAlfil:\r
1978         return PM_BE;\r
1979     case BlackWazir:\r
1980         return PM_BW;\r
1981     case BlackUnicorn:\r
1982         return PM_BU;\r
1983     case BlackCannon:\r
1984         return PM_BO;\r
1985     case BlackGrasshopper:\r
1986         return PM_BG;\r
1987     case BlackMan:\r
1988         return PM_BM;\r
1989     case BlackSilver:\r
1990         return PM_BSG;\r
1991     case BlackLance:\r
1992         return PM_BL;\r
1993     case BlackFalcon:\r
1994         return PM_BV;\r
1995     case BlackCobra:\r
1996         return PM_BS;\r
1997     case BlackCardinal:\r
1998         return PM_BAB;\r
1999     case BlackDragon:\r
2000         return PM_BD;\r
2001 \r
2002     case WhiteAngel:\r
2003         return PM_WA;\r
2004     case WhiteMarshall:\r
2005         return PM_WC;\r
2006     case WhiteFerz:\r
2007         return PM_WF;\r
2008     case WhiteNightrider:\r
2009         return PM_WH;\r
2010     case WhiteAlfil:\r
2011         return PM_WE;\r
2012     case WhiteWazir:\r
2013         return PM_WW;\r
2014     case WhiteUnicorn:\r
2015         return PM_WU;\r
2016     case WhiteCannon:\r
2017         return PM_WO;\r
2018     case WhiteGrasshopper:\r
2019         return PM_WG;\r
2020     case WhiteMan:\r
2021         return PM_WM;\r
2022     case WhiteSilver:\r
2023         return PM_WSG;\r
2024     case WhiteLance:\r
2025         return PM_WL;\r
2026     case WhiteFalcon:\r
2027         return PM_WV;\r
2028     case WhiteCobra:\r
2029         return PM_WS;\r
2030     case WhiteCardinal:\r
2031         return PM_WAB;\r
2032     case WhiteDragon:\r
2033         return PM_WD;\r
2034     }\r
2035 \r
2036     return 0;\r
2037 }\r
2038 \r
2039 void CreatePiecesFromFont()\r
2040 {\r
2041     LOGFONT lf;\r
2042     HDC hdc_window = NULL;\r
2043     HDC hdc = NULL;\r
2044     HFONT hfont_old;\r
2045     int fontHeight;\r
2046     int i;\r
2047 \r
2048     if( fontBitmapSquareSize < 0 ) {\r
2049         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2050         return;\r
2051     }\r
2052 \r
2053     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2054             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2055         fontBitmapSquareSize = -1;\r
2056         return;\r
2057     }\r
2058 \r
2059     if( fontBitmapSquareSize != squareSize ) {\r
2060         hdc_window = GetDC( hwndMain );\r
2061         hdc = CreateCompatibleDC( hdc_window );\r
2062 \r
2063         if( hPieceFont != NULL ) {\r
2064             DeleteObject( hPieceFont );\r
2065         }\r
2066         else {\r
2067             for( i=0; i<=(int)BlackKing; i++ ) {\r
2068                 hPieceMask[i] = NULL;\r
2069                 hPieceFace[i] = NULL;\r
2070             }\r
2071         }\r
2072 \r
2073         fontHeight = 75;\r
2074 \r
2075         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2076             fontHeight = appData.fontPieceSize;\r
2077         }\r
2078 \r
2079         fontHeight = (fontHeight * squareSize) / 100;\r
2080 \r
2081         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2082         lf.lfWidth = 0;\r
2083         lf.lfEscapement = 0;\r
2084         lf.lfOrientation = 0;\r
2085         lf.lfWeight = FW_NORMAL;\r
2086         lf.lfItalic = 0;\r
2087         lf.lfUnderline = 0;\r
2088         lf.lfStrikeOut = 0;\r
2089         lf.lfCharSet = DEFAULT_CHARSET;\r
2090         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2091         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2092         lf.lfQuality = PROOF_QUALITY;\r
2093         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2094         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2095         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2096 \r
2097         hPieceFont = CreateFontIndirect( &lf );\r
2098 \r
2099         if( hPieceFont == NULL ) {\r
2100             fontBitmapSquareSize = -2;\r
2101         }\r
2102         else {\r
2103             /* Setup font-to-piece character table */\r
2104             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2105                 /* No (or wrong) global settings, try to detect the font */\r
2106                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2107                     /* Alpha */\r
2108                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2109                 }\r
2110                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2111                     /* DiagramTT* family */\r
2112                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2113                 }\r
2114                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2115                     /* Fairy symbols */\r
2116                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2117                 }\r
2118                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2119                     /* Good Companion (Some characters get warped as literal :-( */\r
2120                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2121                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2122                     SetCharTable(pieceToFontChar, s);\r
2123                 }\r
2124                 else {\r
2125                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2126                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2127                 }\r
2128             }\r
2129 \r
2130             /* Create bitmaps */\r
2131             hfont_old = SelectObject( hdc, hPieceFont );\r
2132             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2133                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2134                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2135 \r
2136             SelectObject( hdc, hfont_old );\r
2137 \r
2138             fontBitmapSquareSize = squareSize;\r
2139         }\r
2140     }\r
2141 \r
2142     if( hdc != NULL ) {\r
2143         DeleteDC( hdc );\r
2144     }\r
2145 \r
2146     if( hdc_window != NULL ) {\r
2147         ReleaseDC( hwndMain, hdc_window );\r
2148     }\r
2149 }\r
2150 \r
2151 HBITMAP\r
2152 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2153 {\r
2154   char name[128], buf[MSG_SIZ];\r
2155 \r
2156     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2157   if(appData.pieceDirectory[0]) {\r
2158     HBITMAP res;\r
2159     snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);\r
2160     res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
2161     if(res) return res;\r
2162   }\r
2163   if (gameInfo.event &&\r
2164       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2165       strcmp(name, "k80s") == 0) {\r
2166     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2167   }\r
2168   return LoadBitmap(hinst, name);\r
2169 }\r
2170 \r
2171 \r
2172 /* Insert a color into the program's logical palette\r
2173    structure.  This code assumes the given color is\r
2174    the result of the RGB or PALETTERGB macro, and it\r
2175    knows how those macros work (which is documented).\r
2176 */\r
2177 VOID\r
2178 InsertInPalette(COLORREF color)\r
2179 {\r
2180   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2181 \r
2182   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2183     DisplayFatalError(_("Too many colors"), 0, 1);\r
2184     pLogPal->palNumEntries--;\r
2185     return;\r
2186   }\r
2187 \r
2188   pe->peFlags = (char) 0;\r
2189   pe->peRed = (char) (0xFF & color);\r
2190   pe->peGreen = (char) (0xFF & (color >> 8));\r
2191   pe->peBlue = (char) (0xFF & (color >> 16));\r
2192   return;\r
2193 }\r
2194 \r
2195 \r
2196 VOID\r
2197 InitDrawingColors()\r
2198 {\r
2199   int i;\r
2200   if (pLogPal == NULL) {\r
2201     /* Allocate enough memory for a logical palette with\r
2202      * PALETTESIZE entries and set the size and version fields\r
2203      * of the logical palette structure.\r
2204      */\r
2205     pLogPal = (NPLOGPALETTE)\r
2206       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2207                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2208     pLogPal->palVersion    = 0x300;\r
2209   }\r
2210   pLogPal->palNumEntries = 0;\r
2211 \r
2212   InsertInPalette(lightSquareColor);\r
2213   InsertInPalette(darkSquareColor);\r
2214   InsertInPalette(whitePieceColor);\r
2215   InsertInPalette(blackPieceColor);\r
2216   InsertInPalette(highlightSquareColor);\r
2217   InsertInPalette(premoveHighlightColor);\r
2218 \r
2219   /*  create a logical color palette according the information\r
2220    *  in the LOGPALETTE structure.\r
2221    */\r
2222   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2223 \r
2224   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2225   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2226   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2227   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2228   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2229   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2230   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2231     for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers\r
2232 \r
2233    /* [AS] Force rendering of the font-based pieces */\r
2234   if( fontBitmapSquareSize > 0 ) {\r
2235     fontBitmapSquareSize = 0;\r
2236   }\r
2237 }\r
2238 \r
2239 \r
2240 int\r
2241 BoardWidth(int boardSize, int n)\r
2242 { /* [HGM] argument n added to allow different width and height */\r
2243   int lineGap = sizeInfo[boardSize].lineGap;\r
2244 \r
2245   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2246       lineGap = appData.overrideLineGap;\r
2247   }\r
2248 \r
2249   return (n + 1) * lineGap +\r
2250           n * sizeInfo[boardSize].squareSize;\r
2251 }\r
2252 \r
2253 /* Respond to board resize by dragging edge */\r
2254 VOID\r
2255 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2256 {\r
2257   BoardSize newSize = NUM_SIZES - 1;\r
2258   static int recurse = 0;\r
2259   if (IsIconic(hwndMain)) return;\r
2260   if (recurse > 0) return;\r
2261   recurse++;\r
2262   while (newSize > 0) {\r
2263         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2264         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2265            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2266     newSize--;\r
2267   } \r
2268   boardSize = newSize;\r
2269   InitDrawingSizes(boardSize, flags);\r
2270   recurse--;\r
2271 }\r
2272 \r
2273 \r
2274 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2275 \r
2276 VOID\r
2277 InitDrawingSizes(BoardSize boardSize, int flags)\r
2278 {\r
2279   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2280   ChessSquare piece;\r
2281   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2282   HDC hdc;\r
2283   SIZE clockSize, messageSize;\r
2284   HFONT oldFont;\r
2285   char buf[MSG_SIZ];\r
2286   char *str;\r
2287   HMENU hmenu = GetMenu(hwndMain);\r
2288   RECT crect, wrect, oldRect;\r
2289   int offby;\r
2290   LOGBRUSH logbrush;\r
2291   VariantClass v = gameInfo.variant;\r
2292 \r
2293   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2294   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2295 \r
2296   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2297   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2298   if(boardSize == -1) return;     // no size defined yet; abort (to allow early call of InitPosition)\r
2299   oldBoardSize = boardSize;\r
2300 \r
2301   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2302   { // correct board size to one where built-in pieces exist\r
2303     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2304        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2305 \r
2306       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2307       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2308       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy || v == VariantLion ) {\r
2309       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2310       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2311                                    boardSize = SizeMiddling;\r
2312     }\r
2313   }\r
2314   if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2315 \r
2316   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2317   oldRect.top = wpMain.y;\r
2318   oldRect.right = wpMain.x + wpMain.width;\r
2319   oldRect.bottom = wpMain.y + wpMain.height;\r
2320 \r
2321   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2322   smallLayout = sizeInfo[boardSize].smallLayout;\r
2323   squareSize = sizeInfo[boardSize].squareSize;\r
2324   lineGap = sizeInfo[boardSize].lineGap;\r
2325   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2326   border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;\r
2327 \r
2328   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2329       lineGap = appData.overrideLineGap;\r
2330   }\r
2331 \r
2332   if (tinyLayout != oldTinyLayout) {\r
2333     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2334     if (tinyLayout) {\r
2335       style &= ~WS_SYSMENU;\r
2336       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2337                  "&Minimize\tCtrl+F4");\r
2338     } else {\r
2339       style |= WS_SYSMENU;\r
2340       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2341     }\r
2342     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2343 \r
2344     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2345       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2346         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2347     }\r
2348     DrawMenuBar(hwndMain);\r
2349   }\r
2350 \r
2351   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;\r
2352   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;\r
2353 \r
2354   /* Get text area sizes */\r
2355   hdc = GetDC(hwndMain);\r
2356   if (appData.clockMode) {\r
2357     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2358   } else {\r
2359     snprintf(buf, MSG_SIZ, _("White"));\r
2360   }\r
2361   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2362   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2363   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2364   str = _("We only care about the height here");\r
2365   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2366   SelectObject(hdc, oldFont);\r
2367   ReleaseDC(hwndMain, hdc);\r
2368 \r
2369   /* Compute where everything goes */\r
2370   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2371         /* [HGM] logo: if either logo is on, reserve space for it */\r
2372         logoHeight =  2*clockSize.cy;\r
2373         leftLogoRect.left   = OUTER_MARGIN;\r
2374         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2375         leftLogoRect.top    = OUTER_MARGIN;\r
2376         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2377 \r
2378         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2379         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2380         rightLogoRect.top    = OUTER_MARGIN;\r
2381         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2382 \r
2383 \r
2384     whiteRect.left = leftLogoRect.right;\r
2385     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2386     whiteRect.top = OUTER_MARGIN;\r
2387     whiteRect.bottom = whiteRect.top + logoHeight;\r
2388 \r
2389     blackRect.right = rightLogoRect.left;\r
2390     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2391     blackRect.top = whiteRect.top;\r
2392     blackRect.bottom = whiteRect.bottom;\r
2393   } else {\r
2394     whiteRect.left = OUTER_MARGIN;\r
2395     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2396     whiteRect.top = OUTER_MARGIN;\r
2397     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2398 \r
2399     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2400     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2401     blackRect.top = whiteRect.top;\r
2402     blackRect.bottom = whiteRect.bottom;\r
2403 \r
2404     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2405   }\r
2406 \r
2407   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2408   if (appData.showButtonBar) {\r
2409     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2410       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2411   } else {\r
2412     messageRect.right = OUTER_MARGIN + boardWidth;\r
2413   }\r
2414   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2415   messageRect.bottom = messageRect.top + messageSize.cy;\r
2416 \r
2417   boardRect.left = OUTER_MARGIN;\r
2418   boardRect.right = boardRect.left + boardWidth;\r
2419   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2420   boardRect.bottom = boardRect.top + boardHeight;\r
2421 \r
2422   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2423   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2424   oldTinyLayout = tinyLayout;\r
2425   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2426   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2427     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2428   winW *= 1 + twoBoards;\r
2429   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2430   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2431   wpMain.height = winH; //       without disturbing window attachments\r
2432   GetWindowRect(hwndMain, &wrect);\r
2433   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2434                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2435 \r
2436   // [HGM] placement: let attached windows follow size change.\r
2437   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2438   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2439   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2440   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2441   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2442 \r
2443   /* compensate if menu bar wrapped */\r
2444   GetClientRect(hwndMain, &crect);\r
2445   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2446   wpMain.height += offby;\r
2447   switch (flags) {\r
2448   case WMSZ_TOPLEFT:\r
2449     SetWindowPos(hwndMain, NULL, \r
2450                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2451                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2452     break;\r
2453 \r
2454   case WMSZ_TOPRIGHT:\r
2455   case WMSZ_TOP:\r
2456     SetWindowPos(hwndMain, NULL, \r
2457                  wrect.left, wrect.bottom - wpMain.height, \r
2458                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2459     break;\r
2460 \r
2461   case WMSZ_BOTTOMLEFT:\r
2462   case WMSZ_LEFT:\r
2463     SetWindowPos(hwndMain, NULL, \r
2464                  wrect.right - wpMain.width, wrect.top, \r
2465                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2466     break;\r
2467 \r
2468   case WMSZ_BOTTOMRIGHT:\r
2469   case WMSZ_BOTTOM:\r
2470   case WMSZ_RIGHT:\r
2471   default:\r
2472     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2473                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2474     break;\r
2475   }\r
2476 \r
2477   hwndPause = NULL;\r
2478   for (i = 0; i < N_BUTTONS; i++) {\r
2479     if (buttonDesc[i].hwnd != NULL) {\r
2480       DestroyWindow(buttonDesc[i].hwnd);\r
2481       buttonDesc[i].hwnd = NULL;\r
2482     }\r
2483     if (appData.showButtonBar) {\r
2484       buttonDesc[i].hwnd =\r
2485         CreateWindow("BUTTON", buttonDesc[i].label,\r
2486                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2487                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2488                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2489                      (HMENU) buttonDesc[i].id,\r
2490                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2491       if (tinyLayout) {\r
2492         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2493                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2494                     MAKELPARAM(FALSE, 0));\r
2495       }\r
2496       if (buttonDesc[i].id == IDM_Pause)\r
2497         hwndPause = buttonDesc[i].hwnd;\r
2498       buttonDesc[i].wndproc = (WNDPROC)\r
2499         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2500     }\r
2501   }\r
2502   if (gridPen != NULL) DeleteObject(gridPen);\r
2503   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2504   if (premovePen != NULL) DeleteObject(premovePen);\r
2505   if (lineGap != 0) {\r
2506     logbrush.lbStyle = BS_SOLID;\r
2507     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2508     gridPen =\r
2509       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2510                    lineGap, &logbrush, 0, NULL);\r
2511     logbrush.lbColor = highlightSquareColor;\r
2512     highlightPen =\r
2513       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2514                    lineGap, &logbrush, 0, NULL);\r
2515 \r
2516     logbrush.lbColor = premoveHighlightColor; \r
2517     premovePen =\r
2518       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2519                    lineGap, &logbrush, 0, NULL);\r
2520 \r
2521     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2522     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2523       gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;\r
2524       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2525         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2526       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2527         BOARD_WIDTH * (squareSize + lineGap) + border;\r
2528       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2529     }\r
2530     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2531       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;\r
2532       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2533         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2534         lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2535       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2536         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;\r
2537       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2538     }\r
2539   }\r
2540 \r
2541   /* [HGM] Licensing requirement */\r
2542 #ifdef GOTHIC\r
2543   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2544 #endif\r
2545 #ifdef FALCON\r
2546   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2547 #endif\r
2548   GothicPopUp( "", VariantNormal);\r
2549 \r
2550 \r
2551 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2552 \r
2553   /* Load piece bitmaps for this board size */\r
2554   for (i=0; i<=2; i++) {\r
2555     for (piece = WhitePawn;\r
2556          (int) piece < (int) BlackPawn;\r
2557          piece = (ChessSquare) ((int) piece + 1)) {\r
2558       if (pieceBitmap[i][piece] != NULL)\r
2559         DeleteObject(pieceBitmap[i][piece]);\r
2560     }\r
2561   }\r
2562 \r
2563   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2564   // Orthodox Chess pieces\r
2565   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2566   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2567   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2568   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2569   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2570   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2571   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2572   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2573   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2574   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2575   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2576   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2577   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2578   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2579   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2580   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2581     // in Shogi, Hijack the unused Queen for Lance\r
2582     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2583     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2584     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2585   } else {\r
2586     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2587     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2588     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2589   }\r
2590 \r
2591   if(squareSize <= 72 && squareSize >= 33) { \r
2592     /* A & C are available in most sizes now */\r
2593     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2594       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2595       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2596       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2597       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2598       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2599       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2600       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2601       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2602       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2603       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2604       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2605       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2606     } else { // Smirf-like\r
2607       if(gameInfo.variant == VariantSChess) {\r
2608         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2609         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2610         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2611       } else {\r
2612         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2613         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2614         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2615       }\r
2616     }\r
2617     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2618       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2619       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2620       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2621     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2622       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2623       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2624       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2625     } else { // WinBoard standard\r
2626       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2627       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2628       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2629     }\r
2630   }\r
2631 \r
2632 \r
2633   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2634     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2635     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2636     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2637     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2638     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2639     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2640     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2641     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2642     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2643     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2644     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2645     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2646     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2647     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2648     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2649     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2650     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2651     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2652     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2653     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2654     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2655     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2656     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2657     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2658     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2659     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2660     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2661     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2662     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2663     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2664     pieceBitmap[0][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "s");\r
2665     pieceBitmap[1][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "o");\r
2666     pieceBitmap[2][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "w");\r
2667 \r
2668     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2669       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2670       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2671       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2672       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2673       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2674       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2675       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2676       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2677       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2678       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2679       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2680       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2681     } else {\r
2682       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2683       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2684       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2685       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2686       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2687       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2688       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2689       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2690       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2691       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2692       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2693       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2694     }\r
2695 \r
2696   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2697     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2698     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2699     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2700     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2701     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2702     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2703     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2704     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2705     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2706     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2707     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2708     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2709     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2710     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2711   }\r
2712 \r
2713 \r
2714   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2715   /* special Shogi support in this size */\r
2716   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2717       for (piece = WhitePawn;\r
2718            (int) piece < (int) BlackPawn;\r
2719            piece = (ChessSquare) ((int) piece + 1)) {\r
2720         if (pieceBitmap[i][piece] != NULL)\r
2721           DeleteObject(pieceBitmap[i][piece]);\r
2722       }\r
2723     }\r
2724   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2725   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2726   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2727   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2728   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2729   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2730   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2731   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2732   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2733   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2734   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2735   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2736   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2737   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2738   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2739   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2740   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2741   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2742   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2743   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2744   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2745   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2746   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2747   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2748   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2749   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2750   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2751   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2752   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2753   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2754   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2755   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2756   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2757   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2758   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2759   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2760   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2761   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2762   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2763   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2764   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2765   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2766   minorSize = 0;\r
2767   }\r
2768 }\r
2769 \r
2770 HBITMAP\r
2771 PieceBitmap(ChessSquare p, int kind)\r
2772 {\r
2773   if ((int) p >= (int) BlackPawn)\r
2774     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2775 \r
2776   return pieceBitmap[kind][(int) p];\r
2777 }\r
2778 \r
2779 /***************************************************************/\r
2780 \r
2781 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2782 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2783 /*\r
2784 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2785 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2786 */\r
2787 \r
2788 VOID\r
2789 SquareToPos(int row, int column, int * x, int * y)\r
2790 {\r
2791   if (flipView) {\r
2792     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
2793     *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;\r
2794   } else {\r
2795     *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;\r
2796     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
2797   }\r
2798 }\r
2799 \r
2800 VOID\r
2801 DrawCoordsOnDC(HDC hdc)\r
2802 {\r
2803   static char files[] = "0123456789012345678901221098765432109876543210";\r
2804   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2805   char str[2] = { NULLCHAR, NULLCHAR };\r
2806   int oldMode, oldAlign, x, y, start, i;\r
2807   HFONT oldFont;\r
2808   HBRUSH oldBrush;\r
2809 \r
2810   if (!appData.showCoords)\r
2811     return;\r
2812 \r
2813   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2814 \r
2815   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2816   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2817   oldAlign = GetTextAlign(hdc);\r
2818   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2819 \r
2820   y = boardRect.top + lineGap;\r
2821   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2822 \r
2823   if(border) {\r
2824     SetTextAlign(hdc, TA_RIGHT|TA_TOP);\r
2825     x += border - lineGap - 4; y += squareSize - 6;\r
2826   } else\r
2827   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2828   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2829     str[0] = files[start + i];\r
2830     ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);\r
2831     y += squareSize + lineGap;\r
2832   }\r
2833 \r
2834   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2835 \r
2836   if(border) {\r
2837     SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2838     x += -border + 4; y += border - squareSize + 6;\r
2839   } else\r
2840   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2841   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2842     str[0] = ranks[start + i];\r
2843     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2844     x += squareSize + lineGap;\r
2845   }    \r
2846 \r
2847   SelectObject(hdc, oldBrush);\r
2848   SetBkMode(hdc, oldMode);\r
2849   SetTextAlign(hdc, oldAlign);\r
2850   SelectObject(hdc, oldFont);\r
2851 }\r
2852 \r
2853 VOID\r
2854 DrawGridOnDC(HDC hdc)\r
2855 {\r
2856   HPEN oldPen;\r
2857  \r
2858   if (lineGap != 0) {\r
2859     oldPen = SelectObject(hdc, gridPen);\r
2860     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2861     SelectObject(hdc, oldPen);\r
2862   }\r
2863 }\r
2864 \r
2865 #define HIGHLIGHT_PEN 0\r
2866 #define PREMOVE_PEN   1\r
2867 \r
2868 VOID\r
2869 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2870 {\r
2871   int x1, y1;\r
2872   HPEN oldPen, hPen;\r
2873   if (lineGap == 0) return;\r
2874   if (flipView) {\r
2875     x1 = boardRect.left +\r
2876       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;\r
2877     y1 = boardRect.top +\r
2878       lineGap/2 + y * (squareSize + lineGap) + border;\r
2879   } else {\r
2880     x1 = boardRect.left +\r
2881       lineGap/2 + x * (squareSize + lineGap) + border;\r
2882     y1 = boardRect.top +\r
2883       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;\r
2884   }\r
2885   hPen = pen ? premovePen : highlightPen;\r
2886   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2887   MoveToEx(hdc, x1, y1, NULL);\r
2888   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2889   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2890   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2891   LineTo(hdc, x1, y1);\r
2892   SelectObject(hdc, oldPen);\r
2893 }\r
2894 \r
2895 VOID\r
2896 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2897 {\r
2898   int i;\r
2899   for (i=0; i<2; i++) {\r
2900     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2901       DrawHighlightOnDC(hdc, TRUE,\r
2902                         h->sq[i].x, h->sq[i].y,\r
2903                         pen);\r
2904   }\r
2905 }\r
2906 \r
2907 /* Note: sqcolor is used only in monoMode */\r
2908 /* Note that this code is largely duplicated in woptions.c,\r
2909    function DrawSampleSquare, so that needs to be updated too */\r
2910 VOID\r
2911 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2912 {\r
2913   HBITMAP oldBitmap;\r
2914   HBRUSH oldBrush;\r
2915   int tmpSize;\r
2916 \r
2917   if (appData.blindfold) return;\r
2918 \r
2919   /* [AS] Use font-based pieces if needed */\r
2920   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2921     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2922     CreatePiecesFromFont();\r
2923 \r
2924     if( fontBitmapSquareSize == squareSize ) {\r
2925         int index = TranslatePieceToFontPiece(piece);\r
2926 \r
2927         SelectObject( tmphdc, hPieceMask[ index ] );\r
2928 \r
2929       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2930         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2931       else\r
2932         BitBlt( hdc,\r
2933             x, y,\r
2934             squareSize, squareSize,\r
2935             tmphdc,\r
2936             0, 0,\r
2937             SRCAND );\r
2938 \r
2939         SelectObject( tmphdc, hPieceFace[ index ] );\r
2940 \r
2941       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2942         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2943       else\r
2944         BitBlt( hdc,\r
2945             x, y,\r
2946             squareSize, squareSize,\r
2947             tmphdc,\r
2948             0, 0,\r
2949             SRCPAINT );\r
2950 \r
2951         return;\r
2952     }\r
2953   }\r
2954 \r
2955   if (appData.monoMode) {\r
2956     SelectObject(tmphdc, PieceBitmap(piece, \r
2957       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2958     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2959            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2960   } else {\r
2961     HBRUSH xBrush = whitePieceBrush;\r
2962     tmpSize = squareSize;\r
2963     if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);\r
2964     if(minorSize &&\r
2965         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2966          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2967       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2968       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2969       x += (squareSize - minorSize)>>1;\r
2970       y += squareSize - minorSize - 2;\r
2971       tmpSize = minorSize;\r
2972     }\r
2973     if (color || appData.allWhite ) {\r
2974       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2975       if( color )\r
2976               oldBrush = SelectObject(hdc, xBrush);\r
2977       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2978       if(appData.upsideDown && color==flipView)\r
2979         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2980       else\r
2981         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2982       /* Use black for outline of white pieces */\r
2983       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2984       if(appData.upsideDown && color==flipView)\r
2985         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2986       else\r
2987         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2988     } else if(appData.pieceDirectory[0]) {\r
2989       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2990       oldBrush = SelectObject(hdc, xBrush);\r
2991       if(appData.upsideDown && color==flipView)\r
2992         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2993       else\r
2994         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2995       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2996       if(appData.upsideDown && color==flipView)\r
2997         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2998       else\r
2999         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3000     } else {\r
3001       /* Use square color for details of black pieces */\r
3002       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3003       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3004       if(appData.upsideDown && !flipView)\r
3005         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3006       else\r
3007         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3008     }\r
3009     SelectObject(hdc, oldBrush);\r
3010     SelectObject(tmphdc, oldBitmap);\r
3011   }\r
3012 }\r
3013 \r
3014 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3015 int GetBackTextureMode( int algo )\r
3016 {\r
3017     int result = BACK_TEXTURE_MODE_DISABLED;\r
3018 \r
3019     switch( algo ) \r
3020     {\r
3021         case BACK_TEXTURE_MODE_PLAIN:\r
3022             result = 1; /* Always use identity map */\r
3023             break;\r
3024         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3025             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3026             break;\r
3027     }\r
3028 \r
3029     return result;\r
3030 }\r
3031 \r
3032 /* \r
3033     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3034     to handle redraws cleanly (as random numbers would always be different).\r
3035 */\r
3036 VOID RebuildTextureSquareInfo()\r
3037 {\r
3038     BITMAP bi;\r
3039     int lite_w = 0;\r
3040     int lite_h = 0;\r
3041     int dark_w = 0;\r
3042     int dark_h = 0;\r
3043     int row;\r
3044     int col;\r
3045 \r
3046     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3047 \r
3048     if( liteBackTexture != NULL ) {\r
3049         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3050             lite_w = bi.bmWidth;\r
3051             lite_h = bi.bmHeight;\r
3052         }\r
3053     }\r
3054 \r
3055     if( darkBackTexture != NULL ) {\r
3056         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3057             dark_w = bi.bmWidth;\r
3058             dark_h = bi.bmHeight;\r
3059         }\r
3060     }\r
3061 \r
3062     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3063         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3064             if( (col + row) & 1 ) {\r
3065                 /* Lite square */\r
3066                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3067                   if( lite_w >= squareSize*BOARD_WIDTH )\r
3068                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
3069                   else\r
3070                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3071                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
3072                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
3073                   else\r
3074                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3075                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3076                 }\r
3077             }\r
3078             else {\r
3079                 /* Dark square */\r
3080                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3081                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3082                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3083                   else\r
3084                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3085                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3086                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3087                   else\r
3088                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3089                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3090                 }\r
3091             }\r
3092         }\r
3093     }\r
3094 }\r
3095 \r
3096 /* [AS] Arrow highlighting support */\r
3097 \r
3098 static double A_WIDTH = 5; /* Width of arrow body */\r
3099 \r
3100 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3101 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3102 \r
3103 static double Sqr( double x )\r
3104 {\r
3105     return x*x;\r
3106 }\r
3107 \r
3108 static int Round( double x )\r
3109 {\r
3110     return (int) (x + 0.5);\r
3111 }\r
3112 \r
3113 /* Draw an arrow between two points using current settings */\r
3114 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3115 {\r
3116     POINT arrow[7];\r
3117     double dx, dy, j, k, x, y;\r
3118 \r
3119     if( d_x == s_x ) {\r
3120         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3121 \r
3122         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3123         arrow[0].y = s_y;\r
3124 \r
3125         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3126         arrow[1].y = d_y - h;\r
3127 \r
3128         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3129         arrow[2].y = d_y - h;\r
3130 \r
3131         arrow[3].x = d_x;\r
3132         arrow[3].y = d_y;\r
3133 \r
3134         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3135         arrow[5].y = d_y - h;\r
3136 \r
3137         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3138         arrow[4].y = d_y - h;\r
3139 \r
3140         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3141         arrow[6].y = s_y;\r
3142     }\r
3143     else if( d_y == s_y ) {\r
3144         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3145 \r
3146         arrow[0].x = s_x;\r
3147         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3148 \r
3149         arrow[1].x = d_x - w;\r
3150         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3151 \r
3152         arrow[2].x = d_x - w;\r
3153         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3154 \r
3155         arrow[3].x = d_x;\r
3156         arrow[3].y = d_y;\r
3157 \r
3158         arrow[5].x = d_x - w;\r
3159         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3160 \r
3161         arrow[4].x = d_x - w;\r
3162         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3163 \r
3164         arrow[6].x = s_x;\r
3165         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3166     }\r
3167     else {\r
3168         /* [AS] Needed a lot of paper for this! :-) */\r
3169         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3170         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3171   \r
3172         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3173 \r
3174         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3175 \r
3176         x = s_x;\r
3177         y = s_y;\r
3178 \r
3179         arrow[0].x = Round(x - j);\r
3180         arrow[0].y = Round(y + j*dx);\r
3181 \r
3182         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3183         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3184 \r
3185         if( d_x > s_x ) {\r
3186             x = (double) d_x - k;\r
3187             y = (double) d_y - k*dy;\r