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