1b8460b4d8517eca8a4bcecfea84e39309a4a9d9
[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     POINT pt;\r
1814     int backColor = whitePieceColor; \r
1815     int foreColor = blackPieceColor;\r
1816     \r
1817     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1818         backColor = appData.fontBackColorWhite;\r
1819         foreColor = appData.fontForeColorWhite;\r
1820     }\r
1821     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1822         backColor = appData.fontBackColorBlack;\r
1823         foreColor = appData.fontForeColorBlack;\r
1824     }\r
1825 \r
1826     /* Mask */\r
1827     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1828 \r
1829     hbm_old = SelectObject( hdc, hbm );\r
1830 \r
1831     rc.left = 0;\r
1832     rc.top = 0;\r
1833     rc.right = squareSize;\r
1834     rc.bottom = squareSize;\r
1835 \r
1836     /* Step 1: background is now black */\r
1837     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1838 \r
1839     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1840 \r
1841     pt.x = (squareSize - sz.cx) / 2;\r
1842     pt.y = (squareSize - sz.cy) / 2;\r
1843 \r
1844     SetBkMode( hdc, TRANSPARENT );\r
1845     SetTextColor( hdc, chroma );\r
1846     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1847     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1848 \r
1849     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1850     /* Step 3: the area outside the piece is filled with white */\r
1851 //    FloodFill( hdc, 0, 0, chroma );\r
1852     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1853     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1854     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1855     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1856     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1857     /* \r
1858         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1859         but if the start point is not inside the piece we're lost!\r
1860         There should be a better way to do this... if we could create a region or path\r
1861         from the fill operation we would be fine for example.\r
1862     */\r
1863 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1864     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1865 \r
1866     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1867         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1868         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1869 \r
1870         SelectObject( dc2, bm2 );\r
1871         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1872         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1873         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1874         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1875         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1876 \r
1877         DeleteDC( dc2 );\r
1878         DeleteObject( bm2 );\r
1879     }\r
1880 \r
1881     SetTextColor( hdc, 0 );\r
1882     /* \r
1883         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1884         draw the piece again in black for safety.\r
1885     */\r
1886     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1887 \r
1888     SelectObject( hdc, hbm_old );\r
1889 \r
1890     if( hPieceMask[index] != NULL ) {\r
1891         DeleteObject( hPieceMask[index] );\r
1892     }\r
1893 \r
1894     hPieceMask[index] = hbm;\r
1895 \r
1896     /* Face */\r
1897     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1898 \r
1899     SelectObject( hdc, hbm );\r
1900 \r
1901     {\r
1902         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1903         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1904         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1905 \r
1906         SelectObject( dc1, hPieceMask[index] );\r
1907         SelectObject( dc2, bm2 );\r
1908         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1909         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1910         \r
1911         /* \r
1912             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1913             the piece background and deletes (makes transparent) the rest.\r
1914             Thanks to that mask, we are free to paint the background with the greates\r
1915             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1916             We use this, to make gradients and give the pieces a "roundish" look.\r
1917         */\r
1918         SetPieceBackground( hdc, backColor, 2 );\r
1919         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1920 \r
1921         DeleteDC( dc2 );\r
1922         DeleteDC( dc1 );\r
1923         DeleteObject( bm2 );\r
1924     }\r
1925 \r
1926     SetTextColor( hdc, foreColor );\r
1927     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1928 \r
1929     SelectObject( hdc, hbm_old );\r
1930 \r
1931     if( hPieceFace[index] != NULL ) {\r
1932         DeleteObject( hPieceFace[index] );\r
1933     }\r
1934 \r
1935     hPieceFace[index] = hbm;\r
1936 }\r
1937 \r
1938 static int TranslatePieceToFontPiece( int piece )\r
1939 {\r
1940     switch( piece ) {\r
1941     case BlackPawn:\r
1942         return PM_BP;\r
1943     case BlackKnight:\r
1944         return PM_BN;\r
1945     case BlackBishop:\r
1946         return PM_BB;\r
1947     case BlackRook:\r
1948         return PM_BR;\r
1949     case BlackQueen:\r
1950         return PM_BQ;\r
1951     case BlackKing:\r
1952         return PM_BK;\r
1953     case WhitePawn:\r
1954         return PM_WP;\r
1955     case WhiteKnight:\r
1956         return PM_WN;\r
1957     case WhiteBishop:\r
1958         return PM_WB;\r
1959     case WhiteRook:\r
1960         return PM_WR;\r
1961     case WhiteQueen:\r
1962         return PM_WQ;\r
1963     case WhiteKing:\r
1964         return PM_WK;\r
1965 \r
1966     case BlackAngel:\r
1967         return PM_BA;\r
1968     case BlackMarshall:\r
1969         return PM_BC;\r
1970     case BlackFerz:\r
1971         return PM_BF;\r
1972     case BlackNightrider:\r
1973         return PM_BH;\r
1974     case BlackAlfil:\r
1975         return PM_BE;\r
1976     case BlackWazir:\r
1977         return PM_BW;\r
1978     case BlackUnicorn:\r
1979         return PM_BU;\r
1980     case BlackCannon:\r
1981         return PM_BO;\r
1982     case BlackGrasshopper:\r
1983         return PM_BG;\r
1984     case BlackMan:\r
1985         return PM_BM;\r
1986     case BlackSilver:\r
1987         return PM_BSG;\r
1988     case BlackLance:\r
1989         return PM_BL;\r
1990     case BlackFalcon:\r
1991         return PM_BV;\r
1992     case BlackCobra:\r
1993         return PM_BS;\r
1994     case BlackCardinal:\r
1995         return PM_BAB;\r
1996     case BlackDragon:\r
1997         return PM_BD;\r
1998 \r
1999     case WhiteAngel:\r
2000         return PM_WA;\r
2001     case WhiteMarshall:\r
2002         return PM_WC;\r
2003     case WhiteFerz:\r
2004         return PM_WF;\r
2005     case WhiteNightrider:\r
2006         return PM_WH;\r
2007     case WhiteAlfil:\r
2008         return PM_WE;\r
2009     case WhiteWazir:\r
2010         return PM_WW;\r
2011     case WhiteUnicorn:\r
2012         return PM_WU;\r
2013     case WhiteCannon:\r
2014         return PM_WO;\r
2015     case WhiteGrasshopper:\r
2016         return PM_WG;\r
2017     case WhiteMan:\r
2018         return PM_WM;\r
2019     case WhiteSilver:\r
2020         return PM_WSG;\r
2021     case WhiteLance:\r
2022         return PM_WL;\r
2023     case WhiteFalcon:\r
2024         return PM_WV;\r
2025     case WhiteCobra:\r
2026         return PM_WS;\r
2027     case WhiteCardinal:\r
2028         return PM_WAB;\r
2029     case WhiteDragon:\r
2030         return PM_WD;\r
2031     }\r
2032 \r
2033     return 0;\r
2034 }\r
2035 \r
2036 void CreatePiecesFromFont()\r
2037 {\r
2038     LOGFONT lf;\r
2039     HDC hdc_window = NULL;\r
2040     HDC hdc = NULL;\r
2041     HFONT hfont_old;\r
2042     int fontHeight;\r
2043     int i;\r
2044 \r
2045     if( fontBitmapSquareSize < 0 ) {\r
2046         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2047         return;\r
2048     }\r
2049 \r
2050     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2051             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2052         fontBitmapSquareSize = -1;\r
2053         return;\r
2054     }\r
2055 \r
2056     if( fontBitmapSquareSize != squareSize ) {\r
2057         hdc_window = GetDC( hwndMain );\r
2058         hdc = CreateCompatibleDC( hdc_window );\r
2059 \r
2060         if( hPieceFont != NULL ) {\r
2061             DeleteObject( hPieceFont );\r
2062         }\r
2063         else {\r
2064             for( i=0; i<=(int)BlackKing; i++ ) {\r
2065                 hPieceMask[i] = NULL;\r
2066                 hPieceFace[i] = NULL;\r
2067             }\r
2068         }\r
2069 \r
2070         fontHeight = 75;\r
2071 \r
2072         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2073             fontHeight = appData.fontPieceSize;\r
2074         }\r
2075 \r
2076         fontHeight = (fontHeight * squareSize) / 100;\r
2077 \r
2078         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2079         lf.lfWidth = 0;\r
2080         lf.lfEscapement = 0;\r
2081         lf.lfOrientation = 0;\r
2082         lf.lfWeight = FW_NORMAL;\r
2083         lf.lfItalic = 0;\r
2084         lf.lfUnderline = 0;\r
2085         lf.lfStrikeOut = 0;\r
2086         lf.lfCharSet = DEFAULT_CHARSET;\r
2087         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2088         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2089         lf.lfQuality = PROOF_QUALITY;\r
2090         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2091         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2092         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2093 \r
2094         hPieceFont = CreateFontIndirect( &lf );\r
2095 \r
2096         if( hPieceFont == NULL ) {\r
2097             fontBitmapSquareSize = -2;\r
2098         }\r
2099         else {\r
2100             /* Setup font-to-piece character table */\r
2101             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2102                 /* No (or wrong) global settings, try to detect the font */\r
2103                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2104                     /* Alpha */\r
2105                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2106                 }\r
2107                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2108                     /* DiagramTT* family */\r
2109                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2110                 }\r
2111                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2112                     /* Fairy symbols */\r
2113                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2114                 }\r
2115                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2116                     /* Good Companion (Some characters get warped as literal :-( */\r
2117                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2118                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2119                     SetCharTable(pieceToFontChar, s);\r
2120                 }\r
2121                 else {\r
2122                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2123                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2124                 }\r
2125             }\r
2126 \r
2127             /* Create bitmaps */\r
2128             hfont_old = SelectObject( hdc, hPieceFont );\r
2129             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2130                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2131                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2132 \r
2133             SelectObject( hdc, hfont_old );\r
2134 \r
2135             fontBitmapSquareSize = squareSize;\r
2136         }\r
2137     }\r
2138 \r
2139     if( hdc != NULL ) {\r
2140         DeleteDC( hdc );\r
2141     }\r
2142 \r
2143     if( hdc_window != NULL ) {\r
2144         ReleaseDC( hwndMain, hdc_window );\r
2145     }\r
2146 }\r
2147 \r
2148 HBITMAP\r
2149 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2150 {\r
2151   char name[128], buf[MSG_SIZ];\r
2152 \r
2153     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2154   if(appData.pieceDirectory[0]) {\r
2155     HBITMAP res;\r
2156     snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);\r
2157     res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
2158     if(res) return res;\r
2159   }\r
2160   if (gameInfo.event &&\r
2161       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2162       strcmp(name, "k80s") == 0) {\r
2163     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2164   }\r
2165   return LoadBitmap(hinst, name);\r
2166 }\r
2167 \r
2168 \r
2169 /* Insert a color into the program's logical palette\r
2170    structure.  This code assumes the given color is\r
2171    the result of the RGB or PALETTERGB macro, and it\r
2172    knows how those macros work (which is documented).\r
2173 */\r
2174 VOID\r
2175 InsertInPalette(COLORREF color)\r
2176 {\r
2177   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2178 \r
2179   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2180     DisplayFatalError(_("Too many colors"), 0, 1);\r
2181     pLogPal->palNumEntries--;\r
2182     return;\r
2183   }\r
2184 \r
2185   pe->peFlags = (char) 0;\r
2186   pe->peRed = (char) (0xFF & color);\r
2187   pe->peGreen = (char) (0xFF & (color >> 8));\r
2188   pe->peBlue = (char) (0xFF & (color >> 16));\r
2189   return;\r
2190 }\r
2191 \r
2192 \r
2193 VOID\r
2194 InitDrawingColors()\r
2195 {\r
2196   int i;\r
2197   if (pLogPal == NULL) {\r
2198     /* Allocate enough memory for a logical palette with\r
2199      * PALETTESIZE entries and set the size and version fields\r
2200      * of the logical palette structure.\r
2201      */\r
2202     pLogPal = (NPLOGPALETTE)\r
2203       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2204                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2205     pLogPal->palVersion    = 0x300;\r
2206   }\r
2207   pLogPal->palNumEntries = 0;\r
2208 \r
2209   InsertInPalette(lightSquareColor);\r
2210   InsertInPalette(darkSquareColor);\r
2211   InsertInPalette(whitePieceColor);\r
2212   InsertInPalette(blackPieceColor);\r
2213   InsertInPalette(highlightSquareColor);\r
2214   InsertInPalette(premoveHighlightColor);\r
2215 \r
2216   /*  create a logical color palette according the information\r
2217    *  in the LOGPALETTE structure.\r
2218    */\r
2219   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2220 \r
2221   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2222   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2223   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2224   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2225   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2226   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2227   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2228     for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers\r
2229 \r
2230    /* [AS] Force rendering of the font-based pieces */\r
2231   if( fontBitmapSquareSize > 0 ) {\r
2232     fontBitmapSquareSize = 0;\r
2233   }\r
2234 }\r
2235 \r
2236 \r
2237 int\r
2238 BoardWidth(int boardSize, int n)\r
2239 { /* [HGM] argument n added to allow different width and height */\r
2240   int lineGap = sizeInfo[boardSize].lineGap;\r
2241 \r
2242   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2243       lineGap = appData.overrideLineGap;\r
2244   }\r
2245 \r
2246   return (n + 1) * lineGap +\r
2247           n * sizeInfo[boardSize].squareSize;\r
2248 }\r
2249 \r
2250 /* Respond to board resize by dragging edge */\r
2251 VOID\r
2252 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2253 {\r
2254   BoardSize newSize = NUM_SIZES - 1;\r
2255   static int recurse = 0;\r
2256   if (IsIconic(hwndMain)) return;\r
2257   if (recurse > 0) return;\r
2258   recurse++;\r
2259   while (newSize > 0) {\r
2260         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2261         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2262            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2263     newSize--;\r
2264   } \r
2265   boardSize = newSize;\r
2266   InitDrawingSizes(boardSize, flags);\r
2267   recurse--;\r
2268 }\r
2269 \r
2270 \r
2271 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2272 \r
2273 VOID\r
2274 InitDrawingSizes(BoardSize boardSize, int flags)\r
2275 {\r
2276   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2277   ChessSquare piece;\r
2278   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2279   HDC hdc;\r
2280   SIZE clockSize, messageSize;\r
2281   HFONT oldFont;\r
2282   char buf[MSG_SIZ];\r
2283   char *str;\r
2284   HMENU hmenu = GetMenu(hwndMain);\r
2285   RECT crect, wrect, oldRect;\r
2286   int offby;\r
2287   LOGBRUSH logbrush;\r
2288   VariantClass v = gameInfo.variant;\r
2289 \r
2290   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2291   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2292 \r
2293   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2294   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2295   if(boardSize == -1) return;     // no size defined yet; abort (to allow early call of InitPosition)\r
2296   oldBoardSize = boardSize;\r
2297 \r
2298   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2299   { // correct board size to one where built-in pieces exist\r
2300     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2301        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2302       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2303       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2304       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy || v == VariantLion ) {\r
2305       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2306       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2307                                    boardSize = SizeMiddling;\r
2308     }\r
2309   }\r
2310   if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2311 \r
2312   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2313   oldRect.top = wpMain.y;\r
2314   oldRect.right = wpMain.x + wpMain.width;\r
2315   oldRect.bottom = wpMain.y + wpMain.height;\r
2316 \r
2317   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2318   smallLayout = sizeInfo[boardSize].smallLayout;\r
2319   squareSize = sizeInfo[boardSize].squareSize;\r
2320   lineGap = sizeInfo[boardSize].lineGap;\r
2321   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2322   border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;\r
2323 \r
2324   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2325       lineGap = appData.overrideLineGap;\r
2326   }\r
2327 \r
2328   if (tinyLayout != oldTinyLayout) {\r
2329     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2330     if (tinyLayout) {\r
2331       style &= ~WS_SYSMENU;\r
2332       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2333                  "&Minimize\tCtrl+F4");\r
2334     } else {\r
2335       style |= WS_SYSMENU;\r
2336       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2337     }\r
2338     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2339 \r
2340     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2341       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2342         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2343     }\r
2344     DrawMenuBar(hwndMain);\r
2345   }\r
2346 \r
2347   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;\r
2348   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;\r
2349 \r
2350   /* Get text area sizes */\r
2351   hdc = GetDC(hwndMain);\r
2352   if (appData.clockMode) {\r
2353     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2354   } else {\r
2355     snprintf(buf, MSG_SIZ, _("White"));\r
2356   }\r
2357   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2358   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2359   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2360   str = _("We only care about the height here");\r
2361   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2362   SelectObject(hdc, oldFont);\r
2363   ReleaseDC(hwndMain, hdc);\r
2364 \r
2365   /* Compute where everything goes */\r
2366   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2367         /* [HGM] logo: if either logo is on, reserve space for it */\r
2368         logoHeight =  2*clockSize.cy;\r
2369         leftLogoRect.left   = OUTER_MARGIN;\r
2370         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2371         leftLogoRect.top    = OUTER_MARGIN;\r
2372         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2373 \r
2374         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2375         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2376         rightLogoRect.top    = OUTER_MARGIN;\r
2377         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2378 \r
2379 \r
2380     whiteRect.left = leftLogoRect.right;\r
2381     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2382     whiteRect.top = OUTER_MARGIN;\r
2383     whiteRect.bottom = whiteRect.top + logoHeight;\r
2384 \r
2385     blackRect.right = rightLogoRect.left;\r
2386     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2387     blackRect.top = whiteRect.top;\r
2388     blackRect.bottom = whiteRect.bottom;\r
2389   } else {\r
2390     whiteRect.left = OUTER_MARGIN;\r
2391     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2392     whiteRect.top = OUTER_MARGIN;\r
2393     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2394 \r
2395     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2396     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2397     blackRect.top = whiteRect.top;\r
2398     blackRect.bottom = whiteRect.bottom;\r
2399 \r
2400     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2401   }\r
2402 \r
2403   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2404   if (appData.showButtonBar) {\r
2405     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2406       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2407   } else {\r
2408     messageRect.right = OUTER_MARGIN + boardWidth;\r
2409   }\r
2410   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2411   messageRect.bottom = messageRect.top + messageSize.cy;\r
2412 \r
2413   boardRect.left = OUTER_MARGIN;\r
2414   boardRect.right = boardRect.left + boardWidth;\r
2415   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2416   boardRect.bottom = boardRect.top + boardHeight;\r
2417 \r
2418   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2419   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2420   oldTinyLayout = tinyLayout;\r
2421   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2422   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2423     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2424   winW *= 1 + twoBoards;\r
2425   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2426   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2427   wpMain.height = winH; //       without disturbing window attachments\r
2428   GetWindowRect(hwndMain, &wrect);\r
2429   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2430                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2431 \r
2432   // [HGM] placement: let attached windows follow size change.\r
2433   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2434   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2435   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2436   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2437   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2438 \r
2439   /* compensate if menu bar wrapped */\r
2440   GetClientRect(hwndMain, &crect);\r
2441   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2442   wpMain.height += offby;\r
2443   switch (flags) {\r
2444   case WMSZ_TOPLEFT:\r
2445     SetWindowPos(hwndMain, NULL, \r
2446                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2447                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2448     break;\r
2449 \r
2450   case WMSZ_TOPRIGHT:\r
2451   case WMSZ_TOP:\r
2452     SetWindowPos(hwndMain, NULL, \r
2453                  wrect.left, wrect.bottom - wpMain.height, \r
2454                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2455     break;\r
2456 \r
2457   case WMSZ_BOTTOMLEFT:\r
2458   case WMSZ_LEFT:\r
2459     SetWindowPos(hwndMain, NULL, \r
2460                  wrect.right - wpMain.width, wrect.top, \r
2461                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2462     break;\r
2463 \r
2464   case WMSZ_BOTTOMRIGHT:\r
2465   case WMSZ_BOTTOM:\r
2466   case WMSZ_RIGHT:\r
2467   default:\r
2468     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2469                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2470     break;\r
2471   }\r
2472 \r
2473   hwndPause = NULL;\r
2474   for (i = 0; i < N_BUTTONS; i++) {\r
2475     if (buttonDesc[i].hwnd != NULL) {\r
2476       DestroyWindow(buttonDesc[i].hwnd);\r
2477       buttonDesc[i].hwnd = NULL;\r
2478     }\r
2479     if (appData.showButtonBar) {\r
2480       buttonDesc[i].hwnd =\r
2481         CreateWindow("BUTTON", buttonDesc[i].label,\r
2482                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2483                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2484                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2485                      (HMENU) buttonDesc[i].id,\r
2486                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2487       if (tinyLayout) {\r
2488         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2489                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2490                     MAKELPARAM(FALSE, 0));\r
2491       }\r
2492       if (buttonDesc[i].id == IDM_Pause)\r
2493         hwndPause = buttonDesc[i].hwnd;\r
2494       buttonDesc[i].wndproc = (WNDPROC)\r
2495         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2496     }\r
2497   }\r
2498   if (gridPen != NULL) DeleteObject(gridPen);\r
2499   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2500   if (premovePen != NULL) DeleteObject(premovePen);\r
2501   if (lineGap != 0) {\r
2502     logbrush.lbStyle = BS_SOLID;\r
2503     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2504     gridPen =\r
2505       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2506                    lineGap, &logbrush, 0, NULL);\r
2507     logbrush.lbColor = highlightSquareColor;\r
2508     highlightPen =\r
2509       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2510                    lineGap, &logbrush, 0, NULL);\r
2511 \r
2512     logbrush.lbColor = premoveHighlightColor; \r
2513     premovePen =\r
2514       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2515                    lineGap, &logbrush, 0, NULL);\r
2516 \r
2517     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2518     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2519       gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;\r
2520       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2521         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2522       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2523         BOARD_WIDTH * (squareSize + lineGap) + border;\r
2524       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2525     }\r
2526     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2527       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;\r
2528       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2529         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2530         lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2531       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2532         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;\r
2533       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2534     }\r
2535   }\r
2536 \r
2537   /* [HGM] Licensing requirement */\r
2538 #ifdef GOTHIC\r
2539   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2540 #endif\r
2541 #ifdef FALCON\r
2542   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2543 #endif\r
2544   GothicPopUp( "", VariantNormal);\r
2545 \r
2546 \r
2547 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2548 \r
2549   /* Load piece bitmaps for this board size */\r
2550   for (i=0; i<=2; i++) {\r
2551     for (piece = WhitePawn;\r
2552          (int) piece < (int) BlackPawn;\r
2553          piece = (ChessSquare) ((int) piece + 1)) {\r
2554       if (pieceBitmap[i][piece] != NULL)\r
2555         DeleteObject(pieceBitmap[i][piece]);\r
2556     }\r
2557   }\r
2558 \r
2559   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2560   // Orthodox Chess pieces\r
2561   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2562   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2563   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2564   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2565   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2566   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2567   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2568   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2569   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2570   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2571   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2572   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2573   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2574   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2575   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2576   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2577     // in Shogi, Hijack the unused Queen for Lance\r
2578     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2579     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2580     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2581   } else {\r
2582     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2583     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2584     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2585   }\r
2586 \r
2587   if(squareSize <= 72 && squareSize >= 33) { \r
2588     /* A & C are available in most sizes now */\r
2589     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2590       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2591       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2592       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2593       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2594       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2595       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2596       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2597       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2598       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2599       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2600       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2601       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2602     } else { // Smirf-like\r
2603       if(gameInfo.variant == VariantSChess) {\r
2604         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2605         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2606         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2607       } else {\r
2608         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2609         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2610         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2611       }\r
2612     }\r
2613     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2614       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2615       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2616       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2617     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2618       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2619       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2620       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2621     } else { // WinBoard standard\r
2622       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2623       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2624       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2625     }\r
2626   }\r
2627 \r
2628 \r
2629   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2630     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2631     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2632     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2633     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2634     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2635     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2636     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2637     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2638     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2639     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2640     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2641     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2642     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2643     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2644     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2645     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2646     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2647     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2648     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2649     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2650     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2651     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2652     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2653     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2654     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2655     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2656     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2657     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2658     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2659     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2660     pieceBitmap[0][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "s");\r
2661     pieceBitmap[1][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "o");\r
2662     pieceBitmap[2][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "w");\r
2663 \r
2664     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2665       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2666       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2667       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2668       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2669       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2670       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2671       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2672       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2673       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2674       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2675       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2676       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2677     } else {\r
2678       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2679       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2680       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2681       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2682       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2683       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2684       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2685       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2686       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2687       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2688       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2689       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2690     }\r
2691 \r
2692   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2693     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2694     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2695     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2696     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2697     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2698     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2699     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2700     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2701     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2702     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2703     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2704     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2705     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2706     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2707   }\r
2708 \r
2709 \r
2710   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2711   /* special Shogi support in this size */\r
2712   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2713       for (piece = WhitePawn;\r
2714            (int) piece < (int) BlackPawn;\r
2715            piece = (ChessSquare) ((int) piece + 1)) {\r
2716         if (pieceBitmap[i][piece] != NULL)\r
2717           DeleteObject(pieceBitmap[i][piece]);\r
2718       }\r
2719     }\r
2720   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2721   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2722   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2723   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2724   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2725   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2726   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2727   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2728   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2729   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2730   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2731   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2732   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2733   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2734   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2735   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2736   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2737   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2738   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2739   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2740   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2741   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2742   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2743   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2744   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2745   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2746   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2747   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2748   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2749   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2750   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2751   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2752   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2753   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2754   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2755   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2756   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2757   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2758   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2759   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2760   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2761   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2762   minorSize = 0;\r
2763   }\r
2764 }\r
2765 \r
2766 HBITMAP\r
2767 PieceBitmap(ChessSquare p, int kind)\r
2768 {\r
2769   if ((int) p >= (int) BlackPawn)\r
2770     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2771 \r
2772   return pieceBitmap[kind][(int) p];\r
2773 }\r
2774 \r
2775 /***************************************************************/\r
2776 \r
2777 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2778 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2779 /*\r
2780 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2781 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2782 */\r
2783 \r
2784 VOID\r
2785 SquareToPos(int row, int column, int * x, int * y)\r
2786 {\r
2787   if (flipView) {\r
2788     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
2789     *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;\r
2790   } else {\r
2791     *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;\r
2792     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
2793   }\r
2794 }\r
2795 \r
2796 VOID\r
2797 DrawCoordsOnDC(HDC hdc)\r
2798 {\r
2799   static char files[] = "0123456789012345678901221098765432109876543210";\r
2800   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2801   char str[2] = { NULLCHAR, NULLCHAR };\r
2802   int oldMode, oldAlign, x, y, start, i;\r
2803   HFONT oldFont;\r
2804   HBRUSH oldBrush;\r
2805 \r
2806   if (!appData.showCoords)\r
2807     return;\r
2808 \r
2809   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2810 \r
2811   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2812   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2813   oldAlign = GetTextAlign(hdc);\r
2814   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2815 \r
2816   y = boardRect.top + lineGap;\r
2817   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2818 \r
2819   if(border) {\r
2820     SetTextAlign(hdc, TA_RIGHT|TA_TOP);\r
2821     x += border - lineGap - 4; y += squareSize - 6;\r
2822   } else\r
2823   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2824   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2825     str[0] = files[start + i];\r
2826     ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);\r
2827     y += squareSize + lineGap;\r
2828   }\r
2829 \r
2830   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2831 \r
2832   if(border) {\r
2833     SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2834     x += -border + 4; y += border - squareSize + 6;\r
2835   } else\r
2836   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2837   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2838     str[0] = ranks[start + i];\r
2839     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2840     x += squareSize + lineGap;\r
2841   }    \r
2842 \r
2843   SelectObject(hdc, oldBrush);\r
2844   SetBkMode(hdc, oldMode);\r
2845   SetTextAlign(hdc, oldAlign);\r
2846   SelectObject(hdc, oldFont);\r
2847 }\r
2848 \r
2849 VOID\r
2850 DrawGridOnDC(HDC hdc)\r
2851 {\r
2852   HPEN oldPen;\r
2853  \r
2854   if (lineGap != 0) {\r
2855     oldPen = SelectObject(hdc, gridPen);\r
2856     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2857     SelectObject(hdc, oldPen);\r
2858   }\r
2859 }\r
2860 \r
2861 #define HIGHLIGHT_PEN 0\r
2862 #define PREMOVE_PEN   1\r
2863 \r
2864 VOID\r
2865 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2866 {\r
2867   int x1, y1;\r
2868   HPEN oldPen, hPen;\r
2869   if (lineGap == 0) return;\r
2870   if (flipView) {\r
2871     x1 = boardRect.left +\r
2872       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;\r
2873     y1 = boardRect.top +\r
2874       lineGap/2 + y * (squareSize + lineGap) + border;\r
2875   } else {\r
2876     x1 = boardRect.left +\r
2877       lineGap/2 + x * (squareSize + lineGap) + border;\r
2878     y1 = boardRect.top +\r
2879       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;\r
2880   }\r
2881   hPen = pen ? premovePen : highlightPen;\r
2882   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2883   MoveToEx(hdc, x1, y1, NULL);\r
2884   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2885   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2886   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2887   LineTo(hdc, x1, y1);\r
2888   SelectObject(hdc, oldPen);\r
2889 }\r
2890 \r
2891 VOID\r
2892 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2893 {\r
2894   int i;\r
2895   for (i=0; i<2; i++) {\r
2896     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2897       DrawHighlightOnDC(hdc, TRUE,\r
2898                         h->sq[i].x, h->sq[i].y,\r
2899                         pen);\r
2900   }\r
2901 }\r
2902 \r
2903 /* Note: sqcolor is used only in monoMode */\r
2904 /* Note that this code is largely duplicated in woptions.c,\r
2905    function DrawSampleSquare, so that needs to be updated too */\r
2906 VOID\r
2907 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2908 {\r
2909   HBITMAP oldBitmap;\r
2910   HBRUSH oldBrush;\r
2911   int tmpSize;\r
2912 \r
2913   if (appData.blindfold) return;\r
2914 \r
2915   /* [AS] Use font-based pieces if needed */\r
2916   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2917     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2918     CreatePiecesFromFont();\r
2919 \r
2920     if( fontBitmapSquareSize == squareSize ) {\r
2921         int index = TranslatePieceToFontPiece(piece);\r
2922 \r
2923         SelectObject( tmphdc, hPieceMask[ index ] );\r
2924 \r
2925       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2926         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2927       else\r
2928         BitBlt( hdc,\r
2929             x, y,\r
2930             squareSize, squareSize,\r
2931             tmphdc,\r
2932             0, 0,\r
2933             SRCAND );\r
2934 \r
2935         SelectObject( tmphdc, hPieceFace[ index ] );\r
2936 \r
2937       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2938         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2939       else\r
2940         BitBlt( hdc,\r
2941             x, y,\r
2942             squareSize, squareSize,\r
2943             tmphdc,\r
2944             0, 0,\r
2945             SRCPAINT );\r
2946 \r
2947         return;\r
2948     }\r
2949   }\r
2950 \r
2951   if (appData.monoMode) {\r
2952     SelectObject(tmphdc, PieceBitmap(piece, \r
2953       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2954     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2955            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2956   } else {\r
2957     HBRUSH xBrush = whitePieceBrush;\r
2958     tmpSize = squareSize;\r
2959     if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);\r
2960     if(minorSize &&\r
2961         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2962          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2963       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2964       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2965       x += (squareSize - minorSize)>>1;\r
2966       y += squareSize - minorSize - 2;\r
2967       tmpSize = minorSize;\r
2968     }\r
2969     if (color || appData.allWhite ) {\r
2970       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2971       if( color )\r
2972               oldBrush = SelectObject(hdc, xBrush);\r
2973       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2974       if(appData.upsideDown && color==flipView)\r
2975         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2976       else\r
2977         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2978       /* Use black for outline of white pieces */\r
2979       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2980       if(appData.upsideDown && color==flipView)\r
2981         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2982       else\r
2983         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2984     } else if(appData.pieceDirectory[0]) {\r
2985       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2986       oldBrush = SelectObject(hdc, xBrush);\r
2987       if(appData.upsideDown && color==flipView)\r
2988         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2989       else\r
2990         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2991       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2992       if(appData.upsideDown && color==flipView)\r
2993         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2994       else\r
2995         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2996     } else {\r
2997       /* Use square color for details of black pieces */\r
2998       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2999       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3000       if(appData.upsideDown && !flipView)\r
3001         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3002       else\r
3003         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3004     }\r
3005     SelectObject(hdc, oldBrush);\r
3006     SelectObject(tmphdc, oldBitmap);\r
3007   }\r
3008 }\r
3009 \r
3010 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3011 int GetBackTextureMode( int algo )\r
3012 {\r
3013     int result = BACK_TEXTURE_MODE_DISABLED;\r
3014 \r
3015     switch( algo ) \r
3016     {\r
3017         case BACK_TEXTURE_MODE_PLAIN:\r
3018             result = 1; /* Always use identity map */\r
3019             break;\r
3020         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3021             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3022             break;\r
3023     }\r
3024 \r
3025     return result;\r
3026 }\r
3027 \r
3028 /* \r
3029     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3030     to handle redraws cleanly (as random numbers would always be different).\r
3031 */\r
3032 VOID RebuildTextureSquareInfo()\r
3033 {\r
3034     BITMAP bi;\r
3035     int lite_w = 0;\r
3036     int lite_h = 0;\r
3037     int dark_w = 0;\r
3038     int dark_h = 0;\r
3039     int row;\r
3040     int col;\r
3041 \r
3042     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3043 \r
3044     if( liteBackTexture != NULL ) {\r
3045         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3046             lite_w = bi.bmWidth;\r
3047             lite_h = bi.bmHeight;\r
3048         }\r
3049     }\r
3050 \r
3051     if( darkBackTexture != NULL ) {\r
3052         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3053             dark_w = bi.bmWidth;\r
3054             dark_h = bi.bmHeight;\r
3055         }\r
3056     }\r
3057 \r
3058     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3059         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3060             if( (col + row) & 1 ) {\r
3061                 /* Lite square */\r
3062                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3063                   if( lite_w >= squareSize*BOARD_WIDTH )\r
3064                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
3065                   else\r
3066                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3067                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
3068                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
3069                   else\r
3070                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3071                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3072                 }\r
3073             }\r
3074             else {\r
3075                 /* Dark square */\r
3076                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3077                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3078                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3079                   else\r
3080                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3081                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3082                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3083                   else\r
3084                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3085                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3086                 }\r
3087             }\r
3088         }\r
3089     }\r
3090 }\r
3091 \r
3092 /* [AS] Arrow highlighting support */\r
3093 \r
3094 static double A_WIDTH = 5; /* Width of arrow body */\r
3095 \r
3096 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3097 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3098 \r
3099 static double Sqr( double x )\r
3100 {\r
3101     return x*x;\r
3102 }\r
3103 \r
3104 static int Round( double x )\r
3105 {\r
3106     return (int) (x + 0.5);\r
3107 }\r
3108 \r
3109 /* Draw an arrow between two points using current settings */\r
3110 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3111 {\r
3112     POINT arrow[7];\r
3113     double dx, dy, j, k, x, y;\r
3114 \r
3115     if( d_x == s_x ) {\r
3116         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3117 \r
3118         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3119         arrow[0].y = s_y;\r
3120 \r
3121         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3122         arrow[1].y = d_y - h;\r
3123 \r
3124         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3125         arrow[2].y = d_y - h;\r
3126 \r
3127         arrow[3].x = d_x;\r
3128         arrow[3].y = d_y;\r
3129 \r
3130         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3131         arrow[5].y = d_y - h;\r
3132 \r
3133         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3134         arrow[4].y = d_y - h;\r
3135 \r
3136         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3137         arrow[6].y = s_y;\r
3138     }\r
3139     else if( d_y == s_y ) {\r
3140         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3141 \r
3142         arrow[0].x = s_x;\r
3143         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3144 \r
3145         arrow[1].x = d_x - w;\r
3146         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3147 \r
3148         arrow[2].x = d_x - w;\r
3149         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3150 \r
3151         arrow[3].x = d_x;\r
3152         arrow[3].y = d_y;\r
3153 \r
3154         arrow[5].x = d_x - w;\r
3155         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3156 \r
3157         arrow[4].x = d_x - w;\r
3158         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3159 \r
3160         arrow[6].x = s_x;\r
3161         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3162     }\r
3163     else {\r
3164         /* [AS] Needed a lot of paper for this! :-) */\r
3165         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3166         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3167   \r
3168         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3169 \r
3170         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3171 \r
3172         x = s_x;\r
3173         y = s_y;\r
3174 \r
3175         arrow[0].x = Round(x - j);\r
3176         arrow[0].y = Round(y + j*dx);\r
3177 \r
3178         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3179         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3180 \r
3181         if( d_x > s_x ) {\r
3182             x = (double) d_x - k;\r
3183             y = (double) d_y - k*dy;\r
3184         }\r
3185         else {\r
3186             x = (double) d_x + k;\r
3187             y = (double) d_y + k*dy;\r
3188         }\r
3189 \r
3190         x = Round(x); y = Round(y); // [HGM] make sure width o