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