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