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