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