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