Fix initial board sizing WB
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts. \r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 #include <io.h>\r
77 \r
78 #if __GNUC__\r
79 #include <errno.h>\r
80 #include <string.h>\r
81 #endif\r
82 \r
83 #include "common.h"\r
84 #include "frontend.h"\r
85 #include "backend.h"\r
86 #include "winboard.h"\r
87 #include "moves.h"\r
88 #include "wclipbrd.h"\r
89 #include "woptions.h"\r
90 #include "wsockerr.h"\r
91 #include "defaults.h"\r
92 #include "help.h"\r
93 #include "wsnap.h"\r
94 \r
95 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
96 \r
97   int myrandom(void);\r
98   void mysrandom(unsigned int seed);\r
99 \r
100 extern int whiteFlag, blackFlag;\r
101 Boolean flipClock = FALSE;\r
102 extern HANDLE chatHandle[];\r
103 extern enum ICS_TYPE ics_type;\r
104 \r
105 int  MySearchPath P((char *installDir, char *name, char *fullname));\r
106 int  MyGetFullPathName P((char *name, char *fullname));\r
107 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
108 VOID NewVariantPopup(HWND hwnd);\r
109 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
110                    /*char*/int promoChar));\r
111 void DisplayMove P((int moveNumber));\r
112 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
113 void ChatPopUp P((char *s));\r
114 typedef struct {\r
115   ChessSquare piece;  \r
116   POINT pos;      /* window coordinates of current pos */\r
117   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
118   POINT from;     /* board coordinates of the piece's orig pos */\r
119   POINT to;       /* board coordinates of the piece's new pos */\r
120 } AnimInfo;\r
121 \r
122 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
123 \r
124 typedef struct {\r
125   POINT start;    /* window coordinates of start pos */\r
126   POINT pos;      /* window coordinates of current pos */\r
127   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
128   POINT from;     /* board coordinates of the piece's orig pos */\r
129   ChessSquare piece;\r
130 } DragInfo;\r
131 \r
132 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
133 \r
134 typedef struct {\r
135   POINT sq[2];    /* board coordinates of from, to squares */\r
136 } HighlightInfo;\r
137 \r
138 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
139 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
140 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
141 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
142 \r
143 typedef struct { // [HGM] atomic\r
144   int fromX, fromY, toX, toY, radius;\r
145 } ExplodeInfo;\r
146 \r
147 static ExplodeInfo explodeInfo;\r
148 \r
149 /* Window class names */\r
150 char szAppName[] = "WinBoard";\r
151 char szConsoleName[] = "WBConsole";\r
152 \r
153 /* Title bar text */\r
154 char szTitle[] = "WinBoard";\r
155 char szConsoleTitle[] = "I C S Interaction";\r
156 \r
157 char *programName;\r
158 char *settingsFileName;\r
159 Boolean saveSettingsOnExit;\r
160 char installDir[MSG_SIZ];\r
161 int errorExitStatus;\r
162 \r
163 BoardSize boardSize;\r
164 Boolean chessProgram;\r
165 //static int boardX, boardY;\r
166 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
167 int squareSize, lineGap, minorSize, border;\r
168 static int winW, winH;\r
169 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
170 static int logoHeight = 0;\r
171 static char messageText[MESSAGE_TEXT_MAX];\r
172 static int clockTimerEvent = 0;\r
173 static int loadGameTimerEvent = 0;\r
174 static int analysisTimerEvent = 0;\r
175 static DelayedEventCallback delayedTimerCallback;\r
176 static int delayedTimerEvent = 0;\r
177 static int buttonCount = 2;\r
178 char *icsTextMenuString;\r
179 char *icsNames;\r
180 char *firstChessProgramNames;\r
181 char *secondChessProgramNames;\r
182 \r
183 #define PALETTESIZE 256\r
184 \r
185 HINSTANCE hInst;          /* current instance */\r
186 Boolean alwaysOnTop = FALSE;\r
187 RECT boardRect;\r
188 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
189   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
190 HPALETTE hPal;\r
191 ColorClass currentColorClass;\r
192 \r
193 static HWND savedHwnd;\r
194 HWND hCommPort = NULL;    /* currently open comm port */\r
195 static HWND hwndPause;    /* pause button */\r
196 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
197 static HBRUSH lightSquareBrush, darkSquareBrush,\r
198   blackSquareBrush, /* [HGM] for band between board and holdings */\r
199   explodeBrush,     /* [HGM] atomic */\r
200   markerBrush,      /* [HGM] markers */\r
201   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
202 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
203 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
204 static HPEN gridPen = NULL;\r
205 static HPEN highlightPen = NULL;\r
206 static HPEN premovePen = NULL;\r
207 static NPLOGPALETTE pLogPal;\r
208 static BOOL paletteChanged = FALSE;\r
209 static HICON iconWhite, iconBlack, iconCurrent;\r
210 static int doingSizing = FALSE;\r
211 static int lastSizing = 0;\r
212 static int prevStderrPort;\r
213 static HBITMAP userLogo;\r
214 \r
215 static HBITMAP liteBackTexture = NULL;\r
216 static HBITMAP darkBackTexture = NULL;\r
217 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
218 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
219 static int backTextureSquareSize = 0;\r
220 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
221 \r
222 #if __GNUC__ && !defined(_winmajor)\r
223 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
224 #else\r
225 \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   if (pLogPal == NULL) {\r
2169     /* Allocate enough memory for a logical palette with\r
2170      * PALETTESIZE entries and set the size and version fields\r
2171      * of the logical palette structure.\r
2172      */\r
2173     pLogPal = (NPLOGPALETTE)\r
2174       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2175                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2176     pLogPal->palVersion    = 0x300;\r
2177   }\r
2178   pLogPal->palNumEntries = 0;\r
2179 \r
2180   InsertInPalette(lightSquareColor);\r
2181   InsertInPalette(darkSquareColor);\r
2182   InsertInPalette(whitePieceColor);\r
2183   InsertInPalette(blackPieceColor);\r
2184   InsertInPalette(highlightSquareColor);\r
2185   InsertInPalette(premoveHighlightColor);\r
2186 \r
2187   /*  create a logical color palette according the information\r
2188    *  in the LOGPALETTE structure.\r
2189    */\r
2190   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2191 \r
2192   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2193   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2194   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2195   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2196   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2197   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2198   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2199   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2200   /* [AS] Force rendering of the font-based pieces */\r
2201   if( fontBitmapSquareSize > 0 ) {\r
2202     fontBitmapSquareSize = 0;\r
2203   }\r
2204 }\r
2205 \r
2206 \r
2207 int\r
2208 BoardWidth(int boardSize, int n)\r
2209 { /* [HGM] argument n added to allow different width and height */\r
2210   int lineGap = sizeInfo[boardSize].lineGap;\r
2211 \r
2212   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2213       lineGap = appData.overrideLineGap;\r
2214   }\r
2215 \r
2216   return (n + 1) * lineGap +\r
2217           n * sizeInfo[boardSize].squareSize;\r
2218 }\r
2219 \r
2220 /* Respond to board resize by dragging edge */\r
2221 VOID\r
2222 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2223 {\r
2224   BoardSize newSize = NUM_SIZES - 1;\r
2225   static int recurse = 0;\r
2226   if (IsIconic(hwndMain)) return;\r
2227   if (recurse > 0) return;\r
2228   recurse++;\r
2229   while (newSize > 0) {\r
2230         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2231         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2232            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2233     newSize--;\r
2234   } \r
2235   boardSize = newSize;\r
2236   InitDrawingSizes(boardSize, flags);\r
2237   recurse--;\r
2238 }\r
2239 \r
2240 \r
2241 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2242 \r
2243 VOID\r
2244 InitDrawingSizes(BoardSize boardSize, int flags)\r
2245 {\r
2246   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2247   ChessSquare piece;\r
2248   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2249   HDC hdc;\r
2250   SIZE clockSize, messageSize;\r
2251   HFONT oldFont;\r
2252   char buf[MSG_SIZ];\r
2253   char *str;\r
2254   HMENU hmenu = GetMenu(hwndMain);\r
2255   RECT crect, wrect, oldRect;\r
2256   int offby;\r
2257   LOGBRUSH logbrush;\r
2258   VariantClass v = gameInfo.variant;\r
2259 \r
2260   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2261   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2262 \r
2263   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2264   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2265   if(boardSize == -1) return;     // no size defined yet; abort (to allow early call of InitPosition)\r
2266   oldBoardSize = boardSize;\r
2267 \r
2268   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2269   { // correct board size to one where built-in pieces exist\r
2270     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2271        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2272       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2273       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2274       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy ) {\r
2275       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2276       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2277                                    boardSize = SizeMiddling;\r
2278     }\r
2279   }\r
2280   if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2281 \r
2282   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2283   oldRect.top = wpMain.y;\r
2284   oldRect.right = wpMain.x + wpMain.width;\r
2285   oldRect.bottom = wpMain.y + wpMain.height;\r
2286 \r
2287   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2288   smallLayout = sizeInfo[boardSize].smallLayout;\r
2289   squareSize = sizeInfo[boardSize].squareSize;\r
2290   lineGap = sizeInfo[boardSize].lineGap;\r
2291   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2292   border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;\r
2293 \r
2294   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2295       lineGap = appData.overrideLineGap;\r
2296   }\r
2297 \r
2298   if (tinyLayout != oldTinyLayout) {\r
2299     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2300     if (tinyLayout) {\r
2301       style &= ~WS_SYSMENU;\r
2302       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2303                  "&Minimize\tCtrl+F4");\r
2304     } else {\r
2305       style |= WS_SYSMENU;\r
2306       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2307     }\r
2308     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2309 \r
2310     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2311       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2312         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2313     }\r
2314     DrawMenuBar(hwndMain);\r
2315   }\r
2316 \r
2317   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;\r
2318   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;\r
2319 \r
2320   /* Get text area sizes */\r
2321   hdc = GetDC(hwndMain);\r
2322   if (appData.clockMode) {\r
2323     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2324   } else {\r
2325     snprintf(buf, MSG_SIZ, _("White"));\r
2326   }\r
2327   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2328   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2329   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2330   str = _("We only care about the height here");\r
2331   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2332   SelectObject(hdc, oldFont);\r
2333   ReleaseDC(hwndMain, hdc);\r
2334 \r
2335   /* Compute where everything goes */\r
2336   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2337         /* [HGM] logo: if either logo is on, reserve space for it */\r
2338         logoHeight =  2*clockSize.cy;\r
2339         leftLogoRect.left   = OUTER_MARGIN;\r
2340         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2341         leftLogoRect.top    = OUTER_MARGIN;\r
2342         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2343 \r
2344         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2345         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2346         rightLogoRect.top    = OUTER_MARGIN;\r
2347         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2348 \r
2349 \r
2350     whiteRect.left = leftLogoRect.right;\r
2351     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2352     whiteRect.top = OUTER_MARGIN;\r
2353     whiteRect.bottom = whiteRect.top + logoHeight;\r
2354 \r
2355     blackRect.right = rightLogoRect.left;\r
2356     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2357     blackRect.top = whiteRect.top;\r
2358     blackRect.bottom = whiteRect.bottom;\r
2359   } else {\r
2360     whiteRect.left = OUTER_MARGIN;\r
2361     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2362     whiteRect.top = OUTER_MARGIN;\r
2363     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2364 \r
2365     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2366     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2367     blackRect.top = whiteRect.top;\r
2368     blackRect.bottom = whiteRect.bottom;\r
2369 \r
2370     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2371   }\r
2372 \r
2373   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2374   if (appData.showButtonBar) {\r
2375     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2376       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2377   } else {\r
2378     messageRect.right = OUTER_MARGIN + boardWidth;\r
2379   }\r
2380   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2381   messageRect.bottom = messageRect.top + messageSize.cy;\r
2382 \r
2383   boardRect.left = OUTER_MARGIN;\r
2384   boardRect.right = boardRect.left + boardWidth;\r
2385   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2386   boardRect.bottom = boardRect.top + boardHeight;\r
2387 \r
2388   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2389   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2390   oldTinyLayout = tinyLayout;\r
2391   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2392   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2393     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2394   winW *= 1 + twoBoards;\r
2395   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2396   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2397   wpMain.height = winH; //       without disturbing window attachments\r
2398   GetWindowRect(hwndMain, &wrect);\r
2399   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2400                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2401 \r
2402   // [HGM] placement: let attached windows follow size change.\r
2403   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2404   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2405   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2406   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2407   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2408 \r
2409   /* compensate if menu bar wrapped */\r
2410   GetClientRect(hwndMain, &crect);\r
2411   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2412   wpMain.height += offby;\r
2413   switch (flags) {\r
2414   case WMSZ_TOPLEFT:\r
2415     SetWindowPos(hwndMain, NULL, \r
2416                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2417                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2418     break;\r
2419 \r
2420   case WMSZ_TOPRIGHT:\r
2421   case WMSZ_TOP:\r
2422     SetWindowPos(hwndMain, NULL, \r
2423                  wrect.left, wrect.bottom - wpMain.height, \r
2424                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2425     break;\r
2426 \r
2427   case WMSZ_BOTTOMLEFT:\r
2428   case WMSZ_LEFT:\r
2429     SetWindowPos(hwndMain, NULL, \r
2430                  wrect.right - wpMain.width, wrect.top, \r
2431                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2432     break;\r
2433 \r
2434   case WMSZ_BOTTOMRIGHT:\r
2435   case WMSZ_BOTTOM:\r
2436   case WMSZ_RIGHT:\r
2437   default:\r
2438     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2439                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2440     break;\r
2441   }\r
2442 \r
2443   hwndPause = NULL;\r
2444   for (i = 0; i < N_BUTTONS; i++) {\r
2445     if (buttonDesc[i].hwnd != NULL) {\r
2446       DestroyWindow(buttonDesc[i].hwnd);\r
2447       buttonDesc[i].hwnd = NULL;\r
2448     }\r
2449     if (appData.showButtonBar) {\r
2450       buttonDesc[i].hwnd =\r
2451         CreateWindow("BUTTON", buttonDesc[i].label,\r
2452                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2453                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2454                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2455                      (HMENU) buttonDesc[i].id,\r
2456                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2457       if (tinyLayout) {\r
2458         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2459                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2460                     MAKELPARAM(FALSE, 0));\r
2461       }\r
2462       if (buttonDesc[i].id == IDM_Pause)\r
2463         hwndPause = buttonDesc[i].hwnd;\r
2464       buttonDesc[i].wndproc = (WNDPROC)\r
2465         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2466     }\r
2467   }\r
2468   if (gridPen != NULL) DeleteObject(gridPen);\r
2469   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2470   if (premovePen != NULL) DeleteObject(premovePen);\r
2471   if (lineGap != 0) {\r
2472     logbrush.lbStyle = BS_SOLID;\r
2473     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2474     gridPen =\r
2475       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2476                    lineGap, &logbrush, 0, NULL);\r
2477     logbrush.lbColor = highlightSquareColor;\r
2478     highlightPen =\r
2479       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2480                    lineGap, &logbrush, 0, NULL);\r
2481 \r
2482     logbrush.lbColor = premoveHighlightColor; \r
2483     premovePen =\r
2484       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2485                    lineGap, &logbrush, 0, NULL);\r
2486 \r
2487     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2488     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2489       gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;\r
2490       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2491         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2492       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2493         BOARD_WIDTH * (squareSize + lineGap) + border;\r
2494       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2495     }\r
2496     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2497       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;\r
2498       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2499         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2500         lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2501       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2502         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;\r
2503       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2504     }\r
2505   }\r
2506 \r
2507   /* [HGM] Licensing requirement */\r
2508 #ifdef GOTHIC\r
2509   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2510 #endif\r
2511 #ifdef FALCON\r
2512   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2513 #endif\r
2514   GothicPopUp( "", VariantNormal);\r
2515 \r
2516 \r
2517 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2518 \r
2519   /* Load piece bitmaps for this board size */\r
2520   for (i=0; i<=2; i++) {\r
2521     for (piece = WhitePawn;\r
2522          (int) piece < (int) BlackPawn;\r
2523          piece = (ChessSquare) ((int) piece + 1)) {\r
2524       if (pieceBitmap[i][piece] != NULL)\r
2525         DeleteObject(pieceBitmap[i][piece]);\r
2526     }\r
2527   }\r
2528 \r
2529   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2530   // Orthodox Chess pieces\r
2531   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2532   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2533   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2534   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2535   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2536   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2537   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2538   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2539   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2540   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2541   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2542   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2543   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2544   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2545   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2546   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2547     // in Shogi, Hijack the unused Queen for Lance\r
2548     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2549     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2550     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2551   } else {\r
2552     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2553     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2554     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2555   }\r
2556 \r
2557   if(squareSize <= 72 && squareSize >= 33) { \r
2558     /* A & C are available in most sizes now */\r
2559     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2560       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2561       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2562       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2563       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2564       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2565       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2566       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2567       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2568       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2569       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2570       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2571       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2572     } else { // Smirf-like\r
2573       if(gameInfo.variant == VariantSChess) {\r
2574         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2575         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2576         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2577       } else {\r
2578         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2579         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2580         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2581       }\r
2582     }\r
2583     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2584       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2585       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2586       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2587     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2588       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2589       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2590       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2591     } else { // WinBoard standard\r
2592       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2593       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2594       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2595     }\r
2596   }\r
2597 \r
2598 \r
2599   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2600     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2601     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2602     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2603     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2604     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2605     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2606     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2607     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2608     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2609     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2610     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2611     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2612     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2613     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2614     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2615     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2616     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2617     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2618     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2619     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2620     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2621     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2622     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2623     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2624     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2625     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2626     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2627     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2628     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2629     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2630 \r
2631     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2632       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2633       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2634       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2635       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2636       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2637       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2638       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2639       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2640       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2641       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2642       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2643       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2644     } else {\r
2645       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2646       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2647       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2648       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2649       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2650       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2651       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2652       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2653       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2654       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2655       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2656       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2657     }\r
2658 \r
2659   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2660     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2661     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2662     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2663     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2664     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2665     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2666     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2667     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2668     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2669     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2670     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2671     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2672     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2673     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2674   }\r
2675 \r
2676 \r
2677   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2678   /* special Shogi support in this size */\r
2679   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2680       for (piece = WhitePawn;\r
2681            (int) piece < (int) BlackPawn;\r
2682            piece = (ChessSquare) ((int) piece + 1)) {\r
2683         if (pieceBitmap[i][piece] != NULL)\r
2684           DeleteObject(pieceBitmap[i][piece]);\r
2685       }\r
2686     }\r
2687   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2688   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2689   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2690   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2691   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2692   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2693   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2694   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2695   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2696   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2697   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2698   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2699   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2700   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2701   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2702   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2703   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2704   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2705   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2706   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2707   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2708   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2709   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2710   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2711   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2712   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2713   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2714   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2715   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2716   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2717   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2718   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2719   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2720   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2721   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2722   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2723   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2724   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2725   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2726   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2727   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2728   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2729   minorSize = 0;\r
2730   }\r
2731 }\r
2732 \r
2733 HBITMAP\r
2734 PieceBitmap(ChessSquare p, int kind)\r
2735 {\r
2736   if ((int) p >= (int) BlackPawn)\r
2737     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2738 \r
2739   return pieceBitmap[kind][(int) p];\r
2740 }\r
2741 \r
2742 /***************************************************************/\r
2743 \r
2744 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2745 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2746 /*\r
2747 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2748 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2749 */\r
2750 \r
2751 VOID\r
2752 SquareToPos(int row, int column, int * x, int * y)\r
2753 {\r
2754   if (flipView) {\r
2755     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
2756     *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;\r
2757   } else {\r
2758     *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;\r
2759     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
2760   }\r
2761 }\r
2762 \r
2763 VOID\r
2764 DrawCoordsOnDC(HDC hdc)\r
2765 {\r
2766   static char files[] = "0123456789012345678901221098765432109876543210";\r
2767   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2768   char str[2] = { NULLCHAR, NULLCHAR };\r
2769   int oldMode, oldAlign, x, y, start, i;\r
2770   HFONT oldFont;\r
2771   HBRUSH oldBrush;\r
2772 \r
2773   if (!appData.showCoords)\r
2774     return;\r
2775 \r
2776   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2777 \r
2778   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2779   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2780   oldAlign = GetTextAlign(hdc);\r
2781   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2782 \r
2783   y = boardRect.top + lineGap;\r
2784   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2785 \r
2786   if(border) {\r
2787     SetTextAlign(hdc, TA_RIGHT|TA_TOP);\r
2788     x += border - lineGap - 4; y += squareSize - 6;\r
2789   } else\r
2790   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2791   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2792     str[0] = files[start + i];\r
2793     ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);\r
2794     y += squareSize + lineGap;\r
2795   }\r
2796 \r
2797   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2798 \r
2799   if(border) {\r
2800     SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2801     x += -border + 4; y += border - squareSize + 6;\r
2802   } else\r
2803   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2804   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2805     str[0] = ranks[start + i];\r
2806     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2807     x += squareSize + lineGap;\r
2808   }    \r
2809 \r
2810   SelectObject(hdc, oldBrush);\r
2811   SetBkMode(hdc, oldMode);\r
2812   SetTextAlign(hdc, oldAlign);\r
2813   SelectObject(hdc, oldFont);\r
2814 }\r
2815 \r
2816 VOID\r
2817 DrawGridOnDC(HDC hdc)\r
2818 {\r
2819   HPEN oldPen;\r
2820  \r
2821   if (lineGap != 0) {\r
2822     oldPen = SelectObject(hdc, gridPen);\r
2823     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2824     SelectObject(hdc, oldPen);\r
2825   }\r
2826 }\r
2827 \r
2828 #define HIGHLIGHT_PEN 0\r
2829 #define PREMOVE_PEN   1\r
2830 \r
2831 VOID\r
2832 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2833 {\r
2834   int x1, y1;\r
2835   HPEN oldPen, hPen;\r
2836   if (lineGap == 0) return;\r
2837   if (flipView) {\r
2838     x1 = boardRect.left +\r
2839       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;\r
2840     y1 = boardRect.top +\r
2841       lineGap/2 + y * (squareSize + lineGap) + border;\r
2842   } else {\r
2843     x1 = boardRect.left +\r
2844       lineGap/2 + x * (squareSize + lineGap) + border;\r
2845     y1 = boardRect.top +\r
2846       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;\r
2847   }\r
2848   hPen = pen ? premovePen : highlightPen;\r
2849   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2850   MoveToEx(hdc, x1, y1, NULL);\r
2851   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2852   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2853   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2854   LineTo(hdc, x1, y1);\r
2855   SelectObject(hdc, oldPen);\r
2856 }\r
2857 \r
2858 VOID\r
2859 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2860 {\r
2861   int i;\r
2862   for (i=0; i<2; i++) {\r
2863     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2864       DrawHighlightOnDC(hdc, TRUE,\r
2865                         h->sq[i].x, h->sq[i].y,\r
2866                         pen);\r
2867   }\r
2868 }\r
2869 \r
2870 /* Note: sqcolor is used only in monoMode */\r
2871 /* Note that this code is largely duplicated in woptions.c,\r
2872    function DrawSampleSquare, so that needs to be updated too */\r
2873 VOID\r
2874 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2875 {\r
2876   HBITMAP oldBitmap;\r
2877   HBRUSH oldBrush;\r
2878   int tmpSize;\r
2879 \r
2880   if (appData.blindfold) return;\r
2881 \r
2882   /* [AS] Use font-based pieces if needed */\r
2883   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2884     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2885     CreatePiecesFromFont();\r
2886 \r
2887     if( fontBitmapSquareSize == squareSize ) {\r
2888         int index = TranslatePieceToFontPiece(piece);\r
2889 \r
2890         SelectObject( tmphdc, hPieceMask[ index ] );\r
2891 \r
2892       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2893         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2894       else\r
2895         BitBlt( hdc,\r
2896             x, y,\r
2897             squareSize, squareSize,\r
2898             tmphdc,\r
2899             0, 0,\r
2900             SRCAND );\r
2901 \r
2902         SelectObject( tmphdc, hPieceFace[ index ] );\r
2903 \r
2904       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2905         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2906       else\r
2907         BitBlt( hdc,\r
2908             x, y,\r
2909             squareSize, squareSize,\r
2910             tmphdc,\r
2911             0, 0,\r
2912             SRCPAINT );\r
2913 \r
2914         return;\r
2915     }\r
2916   }\r
2917 \r
2918   if (appData.monoMode) {\r
2919     SelectObject(tmphdc, PieceBitmap(piece, \r
2920       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2921     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2922            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2923   } else {\r
2924     HBRUSH xBrush = whitePieceBrush;\r
2925     tmpSize = squareSize;\r
2926     if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);\r
2927     if(minorSize &&\r
2928         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2929          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2930       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2931       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2932       x += (squareSize - minorSize)>>1;\r
2933       y += squareSize - minorSize - 2;\r
2934       tmpSize = minorSize;\r
2935     }\r
2936     if (color || appData.allWhite ) {\r
2937       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2938       if( color )\r
2939               oldBrush = SelectObject(hdc, xBrush);\r
2940       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2941       if(appData.upsideDown && color==flipView)\r
2942         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2943       else\r
2944         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2945       /* Use black for outline of white pieces */\r
2946       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2947       if(appData.upsideDown && color==flipView)\r
2948         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2949       else\r
2950         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2951     } else if(appData.pieceDirectory[0]) {\r
2952       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2953       oldBrush = SelectObject(hdc, xBrush);\r
2954       if(appData.upsideDown && color==flipView)\r
2955         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2956       else\r
2957         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2958       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2959       if(appData.upsideDown && color==flipView)\r
2960         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2961       else\r
2962         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2963     } else {\r
2964       /* Use square color for details of black pieces */\r
2965       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2966       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2967       if(appData.upsideDown && !flipView)\r
2968         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2969       else\r
2970         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2971     }\r
2972     SelectObject(hdc, oldBrush);\r
2973     SelectObject(tmphdc, oldBitmap);\r
2974   }\r
2975 }\r
2976 \r
2977 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2978 int GetBackTextureMode( int algo )\r
2979 {\r
2980     int result = BACK_TEXTURE_MODE_DISABLED;\r
2981 \r
2982     switch( algo ) \r
2983     {\r
2984         case BACK_TEXTURE_MODE_PLAIN:\r
2985             result = 1; /* Always use identity map */\r
2986             break;\r
2987         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2988             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2989             break;\r
2990     }\r
2991 \r
2992     return result;\r
2993 }\r
2994 \r
2995 /* \r
2996     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2997     to handle redraws cleanly (as random numbers would always be different).\r
2998 */\r
2999 VOID RebuildTextureSquareInfo()\r
3000 {\r
3001     BITMAP bi;\r
3002     int lite_w = 0;\r
3003     int lite_h = 0;\r
3004     int dark_w = 0;\r
3005     int dark_h = 0;\r
3006     int row;\r
3007     int col;\r
3008 \r
3009     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3010 \r
3011     if( liteBackTexture != NULL ) {\r
3012         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3013             lite_w = bi.bmWidth;\r
3014             lite_h = bi.bmHeight;\r
3015         }\r
3016     }\r
3017 \r
3018     if( darkBackTexture != NULL ) {\r
3019         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3020             dark_w = bi.bmWidth;\r
3021             dark_h = bi.bmHeight;\r
3022         }\r
3023     }\r
3024 \r
3025     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3026         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3027             if( (col + row) & 1 ) {\r
3028                 /* Lite square */\r
3029                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3030                   if( lite_w >= squareSize*BOARD_WIDTH )\r
3031                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
3032                   else\r
3033                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3034                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
3035                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
3036                   else\r
3037                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3038                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3039                 }\r
3040             }\r
3041             else {\r
3042                 /* Dark square */\r
3043                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3044                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3045                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3046                   else\r
3047                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3048                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3049                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3050                   else\r
3051                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3052                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3053                 }\r
3054             }\r
3055         }\r
3056     }\r
3057 }\r
3058 \r
3059 /* [AS] Arrow highlighting support */\r
3060 \r
3061 static double A_WIDTH = 5; /* Width of arrow body */\r
3062 \r
3063 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3064 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3065 \r
3066 static double Sqr( double x )\r
3067 {\r
3068     return x*x;\r
3069 }\r
3070 \r
3071 static int Round( double x )\r
3072 {\r
3073     return (int) (x + 0.5);\r
3074 }\r
3075 \r
3076 /* Draw an arrow between two points using current settings */\r
3077 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3078 {\r
3079     POINT arrow[7];\r
3080     double dx, dy, j, k, x, y;\r
3081 \r
3082     if( d_x == s_x ) {\r
3083         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3084 \r
3085         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3086         arrow[0].y = s_y;\r
3087 \r
3088         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3089         arrow[1].y = d_y - h;\r
3090 \r
3091         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3092         arrow[2].y = d_y - h;\r
3093 \r
3094         arrow[3].x = d_x;\r
3095         arrow[3].y = d_y;\r
3096 \r
3097         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3098         arrow[5].y = d_y - h;\r
3099 \r
3100         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3101         arrow[4].y = d_y - h;\r
3102 \r
3103         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3104         arrow[6].y = s_y;\r
3105     }\r
3106     else if( d_y == s_y ) {\r
3107         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3108 \r
3109         arrow[0].x = s_x;\r
3110         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3111 \r
3112         arrow[1].x = d_x - w;\r
3113         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3114 \r
3115         arrow[2].x = d_x - w;\r
3116         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3117 \r
3118         arrow[3].x = d_x;\r
3119         arrow[3].y = d_y;\r
3120 \r
3121         arrow[5].x = d_x - w;\r
3122         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3123 \r
3124         arrow[4].x = d_x - w;\r
3125         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3126 \r
3127         arrow[6].x = s_x;\r
3128         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3129     }\r
3130     else {\r
3131         /* [AS] Needed a lot of paper for this! :-) */\r
3132         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3133         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3134   \r
3135         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3136 \r
3137         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3138 \r
3139         x = s_x;\r
3140         y = s_y;\r
3141 \r
3142         arrow[0].x = Round(x - j);\r
3143         arrow[0].y = Round(y + j*dx);\r
3144 \r
3145         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3146         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3147 \r
3148         if( d_x > s_x ) {\r
3149             x = (double) d_x - k;\r
3150             y = (double) d_y - k*dy;\r
3151         }\r
3152         else {\r
3153             x = (double) d_x + k;\r
3154             y = (double) d_y + k*dy;\r
3155         }\r
3156 \r
3157         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3158 \r
3159         arrow[6].x = Round(x - j);\r
3160         arrow[6].y = Round(y + j*dx);\r
3161 \r
3162         arrow[2].x = Round(arrow[6].x + 2*j);\r
3163         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3164 \r
3165         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3166         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3167 \r
3168         arrow[4].x = d_x;\r
3169         arrow[4].y = d_y;\r
3170 \r
3171         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3172         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3173     }\r
3174 \r
3175     Polygon( hdc, arrow, 7 );\r
3176 }\r
3177 \r
3178 /* [AS] Draw an arrow between two squares */\r
3179 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3180 {\r
3181     int s_x, s_y, d_x, d_y;\r
3182     HPEN hpen;\r
3183     HPEN holdpen;\r
3184     HBRUSH hbrush;\r
3185     HBRUSH holdbrush;\r
3186     LOGBRUSH stLB;\r
3187 \r
3188     if( s_col == d_col && s_row == d_row ) {\r
3189         return;\r
3190     }\r
3191 \r
3192     /* Get source and destination points */\r
3193     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3194     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3195 \r
3196     if( d_y > s_y ) {\r
3197         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3198     }\r
3199     else if( d_y < s_y ) {\r
3200         d_y += squareSize / 2 + squareSize / 4;\r
3201     }\r
3202     else {\r
3203         d_y += squareSize / 2;\r
3204     }\r
3205 \r
3206     if( d_x > s_x ) {\r
3207         d_x += squareSize / 2 - squareSize / 4;\r
3208     }\r
3209     else if( d_x < s_x ) {\r
3210         d_x += squareSize / 2 + squareSize / 4;\r
3211     }\r
3212     else {\r
3213         d_x += squareSize / 2;\r
3214     }\r
3215 \r
3216     s_x += squareSize / 2;\r
3217     s_y += squareSize / 2;\r
3218 \r
3219     /* Adjust width */\r
3220     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3221 \r
3222     /* Draw */\r
3223     stLB.lbStyle = BS_SOLID;\r
3224     stLB.lbColor = appData.highlightArrowColor;\r
3225     stLB.lbHatch = 0;\r
3226 \r
3227     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3228     holdpen = SelectObject( hdc, hpen );\r
3229     hbrush = CreateBrushIndirect( &stLB );\r
3230     holdbrush = SelectObject( hdc, hbrush );\r
3231 \r
3232     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3233 \r
3234     SelectObject( hdc, holdpen );\r
3235     SelectObject( hdc, holdbrush );\r
3236     DeleteObject( hpen );\r
3237     DeleteObject( hbrush );\r
3238 }\r
3239 \r
3240 BOOL HasHighlightInfo()\r
3241 {\r
3242     BOOL result = FALSE;\r
3243 \r
3244     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3245         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3246     {\r
3247         result = TRUE;\r
3248     }\r
3249 \r
3250     return result;\r
3251 }\r
3252 \r
3253 BOOL IsDrawArrowEnabled()\r
3254 {\r
3255     BOOL result = FALSE;\r
3256 \r
3257     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3258         result = TRUE;\r
3259     }\r
3260 \r
3261     return result;\r
3262 }\r
3263 \r
3264 VOID DrawArrowHighlight( HDC hdc )\r
3265 {\r
3266     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3267         DrawArrowBetweenSquares( hdc,\r
3268             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3269             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3270     }\r
3271 }\r
3272 \r
3273 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3274 {\r
3275     HRGN result = NULL;\r
3276 \r
3277     if( HasHighlightInfo() ) {\r
3278         int x1, y1, x2, y2;\r
3279         int sx, sy, dx, dy;\r
3280 \r
3281         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3282         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3283 \r
3284         sx = MIN( x1, x2 );\r
3285         sy = MIN( y1, y2 );\r
3286         dx = MAX( x1, x2 ) + squareSize;\r
3287         dy = MAX( y1, y2 ) + squareSize;\r
3288 \r
3289         result = CreateRectRgn( sx, sy, dx, dy );\r
3290     }\r
3291 \r
3292     return result;\r
3293 }\r
3294 \r
3295 /*\r
3296     Warning: this function modifies the behavior of several other functions. \r
3297     \r
3298     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3299     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3300     repaint is scattered all over the place, which is not good for features such as\r
3301     "arrow highlighting" that require a full repaint of the board.\r
3302 \r
3303     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3304     user interaction, when speed is not so important) but especially to avoid errors\r
3305     in the displayed graphics.\r
3306 \r
3307     In such patched places, I always try refer to this function so there is a single\r
3308     place to maintain knowledge.\r
3309     \r
3310     To restore the original behavior, just return FALSE unconditionally.\r
3311 */\r
3312 BOOL IsFullRepaintPreferrable()\r
3313 {\r
3314     BOOL result = FALSE;\r
3315 \r
3316     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3317         /* Arrow may appear on the board */\r
3318         result = TRUE;\r
3319     }\r
3320 \r
3321     return result;\r
3322 }\r
3323 \r
3324 /* \r
3325     This function is called by DrawPosition to know whether a full repaint must\r
3326     be forced or not.\r
3327 \r
3328     Only DrawPosition may directly call this function, which makes use of \r
3329     some state information. Other function should call DrawPosition specifying \r
3330     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3331 */\r
3332 BOOL DrawPositionNeedsFullRepaint()\r
3333 {\r
3334     BOOL result = FALSE;\r
3335 \r
3336     /* \r
3337         Probably a slightly better policy would be to trigger a full repaint\r
3338         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3339         but animation is fast enough that it's difficult to notice.\r
3340     */\r
3341     if( animInfo.piece == EmptySquare ) {\r
3342         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3343             result = TRUE;\r
3344         }\r
3345     }\r
3346 \r
3347     return result;\r
3348 }\r
3349 \r
3350 static HBITMAP borderBitmap;\r
3351 \r
3352 VOID\r
3353 DrawBackgroundOnDC(HDC hdc)\r
3354 {\r
3355   \r
3356   BITMAP bi;\r
3357   HDC tmphdc;\r
3358   HBITMAP hbm;\r
3359   static char oldBorder[MSG_SIZ];\r
3360   int w = 600, h = 600, mode;\r
3361 \r
3362   if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid\r
3363     strncpy(oldBorder, appData.border, MSG_SIZ-1);\r
3364     borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
3365   }\r
3366   if(borderBitmap == NULL) { // loading failed, use white\r
3367     FillRect( hdc, &boardRect, whitePieceBrush );\r
3368     return;\r
3369   }\r
3370   tmphdc = CreateCompatibleDC(hdc);\r
3371   hbm = SelectObject(tmphdc, borderBitmap);\r
3372   if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {\r
3373             w = bi.bmWidth;\r
3374             h = bi.bmHeight;\r
3375   }\r
3376   mode = SetStretchBltMode(hdc, COLORONCOLOR);\r
3377   StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left, \r
3378                   boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3379   SetStretchBltMode(hdc, mode);\r
3380   SelectObject(tmphdc, hbm);\r
3381   DeleteDC(tmphdc);\r
3382 }\r
3383 \r
3384 VOID\r
3385 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3386 {\r
3387   int row, column, x, y, square_color, piece_color;\r
3388   ChessSquare piece;\r
3389   HBRUSH oldBrush;\r
3390   HDC texture_hdc = NULL;\r
3391 \r
3392   /* [AS] Initialize background textures if needed */\r
3393   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3394       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3395       if( backTextureSquareSize != squareSize \r
3396        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3397           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3398           backTextureSquareSize = squareSize;\r
3399           RebuildTextureSquareInfo();\r
3400       }\r
3401 \r
3402       texture_hdc = CreateCompatibleDC( hdc );\r
3403   }\r
3404 \r
3405   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3406     for (column = 0; column < BOARD_WIDTH; column++) {\r
3407   \r
3408       SquareToPos(row, column, &x, &y);\r
3409 \r
3410       piece = board[row][column];\r
3411 \r
3412       square_color = ((column + row) % 2) == 1;\r
3413       if( gameInfo.variant == VariantXiangqi ) {\r
3414           square_color = !InPalace(row, column);\r
3415           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3416           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3417       }\r
3418       piece_color = (int) piece < (int) BlackPawn;\r
3419 \r
3420 \r
3421       /* [HGM] holdings file: light square or black */\r
3422       if(column == BOARD_LEFT-2) {\r
3423             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3424                 square_color = 1;\r
3425             else {\r
3426                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3427                 continue;\r
3428             }\r
3429       } else\r
3430       if(column == BOARD_RGHT + 1 ) {\r
3431             if( row < gameInfo.holdingsSize )\r
3432                 square_color = 1;\r
3433             else {\r
3434                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3435                 continue;\r
3436             }\r
3437       }\r
3438       if(column == BOARD_LEFT-1 ) /* left align */\r
3439             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3440       else if( column == BOARD_RGHT) /* right align */\r
3441             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3442       else\r
3443       if (appData.monoMode) {\r
3444         if (piece == EmptySquare) {\r
3445           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3446                  square_color ? WHITENESS : BLACKNESS);\r
3447         } else {\r
3448           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3449         }\r
3450       } \r
3451       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3452           /* [AS] Draw the square using a texture bitmap */\r
3453           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3454           int r = row, c = column; // [HGM] do not flip board in flipView\r
3455           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3456 \r
3457           DrawTile( x, y, \r
3458               squareSize, squareSize, \r
3459               hdc, \r
3460               texture_hdc,\r
3461               backTextureSquareInfo[r][c].mode,\r
3462               backTextureSquareInfo[r][c].x,\r
3463               backTextureSquareInfo[r][c].y );\r
3464 \r
3465           SelectObject( texture_hdc, hbm );\r
3466 \r
3467           if (piece != EmptySquare) {\r
3468               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3469           }\r
3470       }\r
3471       else {\r
3472         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3473 \r
3474         oldBrush = SelectObject(hdc, brush );\r
3475         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3476         SelectObject(hdc, oldBrush);\r
3477         if (piece != EmptySquare)\r
3478           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3479       }\r
3480     }\r
3481   }\r
3482 \r
3483   if( texture_hdc != NULL ) {\r
3484     DeleteDC( texture_hdc );\r
3485   }\r
3486 }\r
3487 \r
3488 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3489 void fputDW(FILE *f, int x)\r
3490 {\r
3491         fputc(x     & 255, f);\r
3492         fputc(x>>8  & 255, f);\r
3493         fputc(x>>16 & 255, f);\r
3494         fputc(x>>24 & 255, f);\r
3495 }\r
3496 \r
3497 #define MAX_CLIPS 200   /* more than enough */\r
3498 \r
3499 VOID\r
3500 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3501 {\r
3502 //  HBITMAP bufferBitmap;\r
3503   BITMAP bi;\r
3504 //  RECT Rect;\r
3505   HDC tmphdc;\r
3506   HBITMAP hbm;\r
3507   int w = 100, h = 50;\r
3508 \r
3509   if(logo == NULL) {\r
3510     if(!logoHeight) return;\r
3511     FillRect( hdc, &logoRect, whitePieceBrush );\r
3512   }\r
3513 //  GetClientRect(hwndMain, &Rect);\r
3514 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3515 //                                      Rect.bottom-Rect.top+1);\r
3516   tmphdc = CreateCompatibleDC(hdc);\r
3517   hbm = SelectObject(tmphdc, logo);\r
3518   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3519             w = bi.bmWidth;\r
3520             h = bi.bmHeight;\r
3521   }\r
3522   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3523                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3524   SelectObject(tmphdc, hbm);\r
3525   DeleteDC(tmphdc);\r
3526 }\r
3527 \r
3528 VOID\r
3529 DisplayLogos()\r
3530 {\r
3531   if(logoHeight) {\r
3532         HDC hdc = GetDC(hwndMain);\r
3533         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3534         if(appData.autoLogo) {\r
3535           \r
3536           switch(gameMode) { // pick logos based on game mode\r
3537             case IcsObserving:\r
3538                 whiteLogo = second.programLogo; // ICS logo\r
3539                 blackLogo = second.programLogo;\r
3540             default:\r
3541                 break;\r
3542             case IcsPlayingWhite:\r
3543                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3544                 blackLogo = second.programLogo; // ICS logo\r
3545                 break;\r
3546             case IcsPlayingBlack:\r
3547                 whiteLogo = second.programLogo; // ICS logo\r
3548                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3549                 break;\r
3550             case TwoMachinesPlay:\r
3551                 if(first.twoMachinesColor[0] == 'b') {\r
3552                     whiteLogo = second.programLogo;\r
3553                     blackLogo = first.programLogo;\r
3554                 }\r
3555                 break;\r
3556             case MachinePlaysWhite:\r
3557                 blackLogo = userLogo;\r
3558                 break;\r
3559             case MachinePlaysBlack:\r
3560                 whiteLogo = userLogo;\r
3561                 blackLogo = first.programLogo;\r
3562           }\r
3563         }\r
3564         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3565         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3566         ReleaseDC(hwndMain, hdc);\r
3567   }\r
3568 }\r
3569 \r
3570 void\r
3571 UpdateLogos(int display)\r
3572 { // called after loading new engine(s), in tourney or from menu\r
3573   LoadLogo(&first, 0, FALSE);\r
3574   LoadLogo(&second, 1, appData.icsActive);\r
3575   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3576   if(display) DisplayLogos();\r
3577 }\r
3578 \r
3579 static HDC hdcSeek;\r
3580 \r
3581 // [HGM] seekgraph\r
3582 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3583 {\r
3584     POINT stPt;\r
3585     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3586     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3587     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3588     SelectObject( hdcSeek, hp );\r
3589 }\r
3590 \r
3591 // front-end wrapper for drawing functions to do rectangles\r
3592 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3593 {\r
3594     HPEN hp;\r
3595     RECT rc;\r
3596 \r
3597     if (hdcSeek == NULL) {\r
3598     hdcSeek = GetDC(hwndMain);\r
3599       if (!appData.monoMode) {\r
3600         SelectPalette(hdcSeek, hPal, FALSE);\r
3601         RealizePalette(hdcSeek);\r
3602       }\r
3603     }\r
3604     hp = SelectObject( hdcSeek, gridPen );\r
3605     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3606     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3607     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3608     SelectObject( hdcSeek, hp );\r
3609 }\r
3610 \r
3611 // front-end wrapper for putting text in graph\r
3612 void DrawSeekText(char *buf, int x, int y)\r
3613 {\r
3614         SIZE stSize;\r
3615         SetBkMode( hdcSeek, TRANSPARENT );\r
3616         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3617         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3618 }\r
3619 \r
3620 void DrawSeekDot(int x, int y, int color)\r
3621 {\r
3622         int square = color & 0x80;\r
3623         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3624                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3625         color &= 0x7F;\r
3626         if(square)\r
3627             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3628                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3629         else\r
3630             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3631                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3632             SelectObject(hdcSeek, oldBrush);\r
3633 }\r
3634 \r
3635 void DrawSeekOpen()\r
3636 {\r
3637 }\r
3638 \r
3639 void DrawSeekClose()\r
3640 {\r
3641 }\r
3642 \r
3643 VOID\r
3644 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3645 {\r
3646   static Board lastReq[2], lastDrawn[2];\r
3647   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3648   static int lastDrawnFlipView = 0;\r
3649   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3650   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3651   HDC tmphdc;\r
3652   HDC hdcmem;\r
3653   HBITMAP bufferBitmap;\r
3654   HBITMAP oldBitmap;\r
3655   RECT Rect;\r
3656   HRGN clips[MAX_CLIPS];\r
3657   ChessSquare dragged_piece = EmptySquare;\r
3658   int nr = twoBoards*partnerUp;\r
3659 \r
3660   /* I'm undecided on this - this function figures out whether a full\r
3661    * repaint is necessary on its own, so there's no real reason to have the\r
3662    * caller tell it that.  I think this can safely be set to FALSE - but\r
3663    * if we trust the callers not to request full repaints unnessesarily, then\r
3664    * we could skip some clipping work.  In other words, only request a full\r
3665    * redraw when the majority of pieces have changed positions (ie. flip, \r
3666    * gamestart and similar)  --Hawk\r
3667    */\r
3668   Boolean fullrepaint = repaint;\r
3669 \r
3670   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3671 \r
3672   if( DrawPositionNeedsFullRepaint() ) {\r
3673       fullrepaint = TRUE;\r
3674   }\r
3675 \r