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