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