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