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