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