Load opponent logo based on handle in ICS play (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;\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];\r
2117 \r
2118     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2119   if (gameInfo.event &&\r
2120       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2121       strcmp(name, "k80s") == 0) {\r
2122     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2123   }\r
2124   return LoadBitmap(hinst, name);\r
2125 }\r
2126 \r
2127 \r
2128 /* Insert a color into the program's logical palette\r
2129    structure.  This code assumes the given color is\r
2130    the result of the RGB or PALETTERGB macro, and it\r
2131    knows how those macros work (which is documented).\r
2132 */\r
2133 VOID\r
2134 InsertInPalette(COLORREF color)\r
2135 {\r
2136   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2137 \r
2138   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2139     DisplayFatalError(_("Too many colors"), 0, 1);\r
2140     pLogPal->palNumEntries--;\r
2141     return;\r
2142   }\r
2143 \r
2144   pe->peFlags = (char) 0;\r
2145   pe->peRed = (char) (0xFF & color);\r
2146   pe->peGreen = (char) (0xFF & (color >> 8));\r
2147   pe->peBlue = (char) (0xFF & (color >> 16));\r
2148   return;\r
2149 }\r
2150 \r
2151 \r
2152 VOID\r
2153 InitDrawingColors()\r
2154 {\r
2155   if (pLogPal == NULL) {\r
2156     /* Allocate enough memory for a logical palette with\r
2157      * PALETTESIZE entries and set the size and version fields\r
2158      * of the logical palette structure.\r
2159      */\r
2160     pLogPal = (NPLOGPALETTE)\r
2161       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2162                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2163     pLogPal->palVersion    = 0x300;\r
2164   }\r
2165   pLogPal->palNumEntries = 0;\r
2166 \r
2167   InsertInPalette(lightSquareColor);\r
2168   InsertInPalette(darkSquareColor);\r
2169   InsertInPalette(whitePieceColor);\r
2170   InsertInPalette(blackPieceColor);\r
2171   InsertInPalette(highlightSquareColor);\r
2172   InsertInPalette(premoveHighlightColor);\r
2173 \r
2174   /*  create a logical color palette according the information\r
2175    *  in the LOGPALETTE structure.\r
2176    */\r
2177   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2178 \r
2179   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2180   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2181   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2182   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2183   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2184   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2185   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2186   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2187   /* [AS] Force rendering of the font-based pieces */\r
2188   if( fontBitmapSquareSize > 0 ) {\r
2189     fontBitmapSquareSize = 0;\r
2190   }\r
2191 }\r
2192 \r
2193 \r
2194 int\r
2195 BoardWidth(int boardSize, int n)\r
2196 { /* [HGM] argument n added to allow different width and height */\r
2197   int lineGap = sizeInfo[boardSize].lineGap;\r
2198 \r
2199   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2200       lineGap = appData.overrideLineGap;\r
2201   }\r
2202 \r
2203   return (n + 1) * lineGap +\r
2204           n * sizeInfo[boardSize].squareSize;\r
2205 }\r
2206 \r
2207 /* Respond to board resize by dragging edge */\r
2208 VOID\r
2209 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2210 {\r
2211   BoardSize newSize = NUM_SIZES - 1;\r
2212   static int recurse = 0;\r
2213   if (IsIconic(hwndMain)) return;\r
2214   if (recurse > 0) return;\r
2215   recurse++;\r
2216   while (newSize > 0) {\r
2217         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2218         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2219            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2220     newSize--;\r
2221   } \r
2222   boardSize = newSize;\r
2223   InitDrawingSizes(boardSize, flags);\r
2224   recurse--;\r
2225 }\r
2226 \r
2227 \r
2228 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2229 \r
2230 VOID\r
2231 InitDrawingSizes(BoardSize boardSize, int flags)\r
2232 {\r
2233   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2234   ChessSquare piece;\r
2235   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2236   HDC hdc;\r
2237   SIZE clockSize, messageSize;\r
2238   HFONT oldFont;\r
2239   char buf[MSG_SIZ];\r
2240   char *str;\r
2241   HMENU hmenu = GetMenu(hwndMain);\r
2242   RECT crect, wrect, oldRect;\r
2243   int offby;\r
2244   LOGBRUSH logbrush;\r
2245   VariantClass v = gameInfo.variant;\r
2246 \r
2247   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2248   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2249 \r
2250   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2251   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2252   oldBoardSize = boardSize;\r
2253 \r
2254   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2255   { // correct board size to one where built-in pieces exist\r
2256     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2257        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2258       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2259       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2260       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy ) {\r
2261       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2262       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2263                                    boardSize = SizeMiddling;\r
2264     }\r
2265   }\r
2266   if(!appData.useFont && boardSize == SizePetite && (v == VariantShogi || v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2267 \r
2268   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2269   oldRect.top = wpMain.y;\r
2270   oldRect.right = wpMain.x + wpMain.width;\r
2271   oldRect.bottom = wpMain.y + wpMain.height;\r
2272 \r
2273   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2274   smallLayout = sizeInfo[boardSize].smallLayout;\r
2275   squareSize = sizeInfo[boardSize].squareSize;\r
2276   lineGap = sizeInfo[boardSize].lineGap;\r
2277   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2278 \r
2279   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2280       lineGap = appData.overrideLineGap;\r
2281   }\r
2282 \r
2283   if (tinyLayout != oldTinyLayout) {\r
2284     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2285     if (tinyLayout) {\r
2286       style &= ~WS_SYSMENU;\r
2287       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2288                  "&Minimize\tCtrl+F4");\r
2289     } else {\r
2290       style |= WS_SYSMENU;\r
2291       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2292     }\r
2293     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2294 \r
2295     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2296       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2297         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2298     }\r
2299     DrawMenuBar(hwndMain);\r
2300   }\r
2301 \r
2302   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2303   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2304 \r
2305   /* Get text area sizes */\r
2306   hdc = GetDC(hwndMain);\r
2307   if (appData.clockMode) {\r
2308     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2309   } else {\r
2310     snprintf(buf, MSG_SIZ, _("White"));\r
2311   }\r
2312   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2313   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2314   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2315   str = _("We only care about the height here");\r
2316   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2317   SelectObject(hdc, oldFont);\r
2318   ReleaseDC(hwndMain, hdc);\r
2319 \r
2320   /* Compute where everything goes */\r
2321   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2322         /* [HGM] logo: if either logo is on, reserve space for it */\r
2323         logoHeight =  2*clockSize.cy;\r
2324         leftLogoRect.left   = OUTER_MARGIN;\r
2325         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2326         leftLogoRect.top    = OUTER_MARGIN;\r
2327         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2328 \r
2329         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2330         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2331         rightLogoRect.top    = OUTER_MARGIN;\r
2332         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2333 \r
2334 \r
2335     whiteRect.left = leftLogoRect.right;\r
2336     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2337     whiteRect.top = OUTER_MARGIN;\r
2338     whiteRect.bottom = whiteRect.top + logoHeight;\r
2339 \r
2340     blackRect.right = rightLogoRect.left;\r
2341     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2342     blackRect.top = whiteRect.top;\r
2343     blackRect.bottom = whiteRect.bottom;\r
2344   } else {\r
2345     whiteRect.left = OUTER_MARGIN;\r
2346     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2347     whiteRect.top = OUTER_MARGIN;\r
2348     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2349 \r
2350     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2351     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2352     blackRect.top = whiteRect.top;\r
2353     blackRect.bottom = whiteRect.bottom;\r
2354 \r
2355     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2356   }\r
2357 \r
2358   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2359   if (appData.showButtonBar) {\r
2360     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2361       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2362   } else {\r
2363     messageRect.right = OUTER_MARGIN + boardWidth;\r
2364   }\r
2365   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2366   messageRect.bottom = messageRect.top + messageSize.cy;\r
2367 \r
2368   boardRect.left = OUTER_MARGIN;\r
2369   boardRect.right = boardRect.left + boardWidth;\r
2370   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2371   boardRect.bottom = boardRect.top + boardHeight;\r
2372 \r
2373   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2374   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2375   oldTinyLayout = tinyLayout;\r
2376   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2377   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2378     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2379   winW *= 1 + twoBoards;\r
2380   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2381   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2382   wpMain.height = winH; //       without disturbing window attachments\r
2383   GetWindowRect(hwndMain, &wrect);\r
2384   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2385                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2386 \r
2387   // [HGM] placement: let attached windows follow size change.\r
2388   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2389   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2390   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2391   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2392   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2393 \r
2394   /* compensate if menu bar wrapped */\r
2395   GetClientRect(hwndMain, &crect);\r
2396   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2397   wpMain.height += offby;\r
2398   switch (flags) {\r
2399   case WMSZ_TOPLEFT:\r
2400     SetWindowPos(hwndMain, NULL, \r
2401                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2402                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2403     break;\r
2404 \r
2405   case WMSZ_TOPRIGHT:\r
2406   case WMSZ_TOP:\r
2407     SetWindowPos(hwndMain, NULL, \r
2408                  wrect.left, wrect.bottom - wpMain.height, \r
2409                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2410     break;\r
2411 \r
2412   case WMSZ_BOTTOMLEFT:\r
2413   case WMSZ_LEFT:\r
2414     SetWindowPos(hwndMain, NULL, \r
2415                  wrect.right - wpMain.width, wrect.top, \r
2416                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2417     break;\r
2418 \r
2419   case WMSZ_BOTTOMRIGHT:\r
2420   case WMSZ_BOTTOM:\r
2421   case WMSZ_RIGHT:\r
2422   default:\r
2423     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2424                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2425     break;\r
2426   }\r
2427 \r
2428   hwndPause = NULL;\r
2429   for (i = 0; i < N_BUTTONS; i++) {\r
2430     if (buttonDesc[i].hwnd != NULL) {\r
2431       DestroyWindow(buttonDesc[i].hwnd);\r
2432       buttonDesc[i].hwnd = NULL;\r
2433     }\r
2434     if (appData.showButtonBar) {\r
2435       buttonDesc[i].hwnd =\r
2436         CreateWindow("BUTTON", buttonDesc[i].label,\r
2437                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2438                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2439                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2440                      (HMENU) buttonDesc[i].id,\r
2441                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2442       if (tinyLayout) {\r
2443         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2444                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2445                     MAKELPARAM(FALSE, 0));\r
2446       }\r
2447       if (buttonDesc[i].id == IDM_Pause)\r
2448         hwndPause = buttonDesc[i].hwnd;\r
2449       buttonDesc[i].wndproc = (WNDPROC)\r
2450         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2451     }\r
2452   }\r
2453   if (gridPen != NULL) DeleteObject(gridPen);\r
2454   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2455   if (premovePen != NULL) DeleteObject(premovePen);\r
2456   if (lineGap != 0) {\r
2457     logbrush.lbStyle = BS_SOLID;\r
2458     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2459     gridPen =\r
2460       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2461                    lineGap, &logbrush, 0, NULL);\r
2462     logbrush.lbColor = highlightSquareColor;\r
2463     highlightPen =\r
2464       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2465                    lineGap, &logbrush, 0, NULL);\r
2466 \r
2467     logbrush.lbColor = premoveHighlightColor; \r
2468     premovePen =\r
2469       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2470                    lineGap, &logbrush, 0, NULL);\r
2471 \r
2472     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2473     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2474       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2475       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2476         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2477       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2478         BOARD_WIDTH * (squareSize + lineGap);\r
2479       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2480     }\r
2481     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2482       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2483       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2484         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2485         lineGap / 2 + (i * (squareSize + lineGap));\r
2486       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2487         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2488       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2489     }\r
2490   }\r
2491 \r
2492   /* [HGM] Licensing requirement */\r
2493 #ifdef GOTHIC\r
2494   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2495 #endif\r
2496 #ifdef FALCON\r
2497   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2498 #endif\r
2499   GothicPopUp( "", VariantNormal);\r
2500 \r
2501 \r
2502 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2503 \r
2504   /* Load piece bitmaps for this board size */\r
2505   for (i=0; i<=2; i++) {\r
2506     for (piece = WhitePawn;\r
2507          (int) piece < (int) BlackPawn;\r
2508          piece = (ChessSquare) ((int) piece + 1)) {\r
2509       if (pieceBitmap[i][piece] != NULL)\r
2510         DeleteObject(pieceBitmap[i][piece]);\r
2511     }\r
2512   }\r
2513 \r
2514   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2515   // Orthodox Chess pieces\r
2516   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2517   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2518   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2519   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2520   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2521   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2522   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2523   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2524   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2525   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2526   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2527   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2528   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2529   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2530   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2531   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2532     // in Shogi, Hijack the unused Queen for Lance\r
2533     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2534     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2535     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2536   } else {\r
2537     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2538     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2539     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2540   }\r
2541 \r
2542   if(squareSize <= 72 && squareSize >= 33) { \r
2543     /* A & C are available in most sizes now */\r
2544     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2545       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2546       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2547       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2548       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2549       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2550       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2551       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2552       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2553       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2554       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2555       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2556       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2557     } else { // Smirf-like\r
2558       if(gameInfo.variant == VariantSChess) {\r
2559         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2560         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2561         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2562       } else {\r
2563         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2564         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2565         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2566       }\r
2567     }\r
2568     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2569       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2570       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2571       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2572     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2573       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2574       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2575       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2576     } else { // WinBoard standard\r
2577       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2578       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2579       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2580     }\r
2581   }\r
2582 \r
2583 \r
2584   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2585     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2586     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2587     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2588     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2589     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2590     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2591     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2592     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2593     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2594     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2595     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2596     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2597     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2598     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2599     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2600     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2601     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2602     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2603     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2604     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2605     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2606     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2607     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2608     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2609     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2610     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2611     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2612     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2613     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2614     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2615 \r
2616     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2617       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2618       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2619       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2620       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2621       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2622       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2623       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2624       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2625       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2626       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2627       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2628       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2629     } else {\r
2630       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2631       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2632       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2633       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2634       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2635       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2636       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2637       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2638       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2639       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2640       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2641       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2642     }\r
2643 \r
2644   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2645     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2646     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2647     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2648     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2649     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2650     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2651     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2652     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2653     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2654     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2655     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2656     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2657     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2658     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2659   }\r
2660 \r
2661 \r
2662   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2663   /* special Shogi support in this size */\r
2664   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2665       for (piece = WhitePawn;\r
2666            (int) piece < (int) BlackPawn;\r
2667            piece = (ChessSquare) ((int) piece + 1)) {\r
2668         if (pieceBitmap[i][piece] != NULL)\r
2669           DeleteObject(pieceBitmap[i][piece]);\r
2670       }\r
2671     }\r
2672   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2673   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2674   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2675   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2676   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2677   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2678   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2679   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2680   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2681   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2682   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2683   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2684   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2685   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2686   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2687   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2688   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2689   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2690   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2691   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2692   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2693   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2694   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2695   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2696   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2697   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2698   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2699   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2700   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2701   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2702   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2703   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2704   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2705   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2706   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2707   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2708   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2709   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2710   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2711   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2712   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2713   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2714   minorSize = 0;\r
2715   }\r
2716 }\r
2717 \r
2718 HBITMAP\r
2719 PieceBitmap(ChessSquare p, int kind)\r
2720 {\r
2721   if ((int) p >= (int) BlackPawn)\r
2722     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2723 \r
2724   return pieceBitmap[kind][(int) p];\r
2725 }\r
2726 \r
2727 /***************************************************************/\r
2728 \r
2729 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2730 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2731 /*\r
2732 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2733 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2734 */\r
2735 \r
2736 VOID\r
2737 SquareToPos(int row, int column, int * x, int * y)\r
2738 {\r
2739   if (flipView) {\r
2740     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2741     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2742   } else {\r
2743     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2744     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2745   }\r
2746 }\r
2747 \r
2748 VOID\r
2749 DrawCoordsOnDC(HDC hdc)\r
2750 {\r
2751   static char files[] = "0123456789012345678901221098765432109876543210";\r
2752   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2753   char str[2] = { NULLCHAR, NULLCHAR };\r
2754   int oldMode, oldAlign, x, y, start, i;\r
2755   HFONT oldFont;\r
2756   HBRUSH oldBrush;\r
2757 \r
2758   if (!appData.showCoords)\r
2759     return;\r
2760 \r
2761   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2762 \r
2763   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2764   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2765   oldAlign = GetTextAlign(hdc);\r
2766   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2767 \r
2768   y = boardRect.top + lineGap;\r
2769   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2770 \r
2771   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2772   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2773     str[0] = files[start + i];\r
2774     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2775     y += squareSize + lineGap;\r
2776   }\r
2777 \r
2778   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2779 \r
2780   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2781   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2782     str[0] = ranks[start + i];\r
2783     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2784     x += squareSize + lineGap;\r
2785   }    \r
2786 \r
2787   SelectObject(hdc, oldBrush);\r
2788   SetBkMode(hdc, oldMode);\r
2789   SetTextAlign(hdc, oldAlign);\r
2790   SelectObject(hdc, oldFont);\r
2791 }\r
2792 \r
2793 VOID\r
2794 DrawGridOnDC(HDC hdc)\r
2795 {\r
2796   HPEN oldPen;\r
2797  \r
2798   if (lineGap != 0) {\r
2799     oldPen = SelectObject(hdc, gridPen);\r
2800     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2801     SelectObject(hdc, oldPen);\r
2802   }\r
2803 }\r
2804 \r
2805 #define HIGHLIGHT_PEN 0\r
2806 #define PREMOVE_PEN   1\r
2807 \r
2808 VOID\r
2809 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2810 {\r
2811   int x1, y1;\r
2812   HPEN oldPen, hPen;\r
2813   if (lineGap == 0) return;\r
2814   if (flipView) {\r
2815     x1 = boardRect.left +\r
2816       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2817     y1 = boardRect.top +\r
2818       lineGap/2 + y * (squareSize + lineGap);\r
2819   } else {\r
2820     x1 = boardRect.left +\r
2821       lineGap/2 + x * (squareSize + lineGap);\r
2822     y1 = boardRect.top +\r
2823       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2824   }\r
2825   hPen = pen ? premovePen : highlightPen;\r
2826   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2827   MoveToEx(hdc, x1, y1, NULL);\r
2828   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2829   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2830   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2831   LineTo(hdc, x1, y1);\r
2832   SelectObject(hdc, oldPen);\r
2833 }\r
2834 \r
2835 VOID\r
2836 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2837 {\r
2838   int i;\r
2839   for (i=0; i<2; i++) {\r
2840     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2841       DrawHighlightOnDC(hdc, TRUE,\r
2842                         h->sq[i].x, h->sq[i].y,\r
2843                         pen);\r
2844   }\r
2845 }\r
2846 \r
2847 /* Note: sqcolor is used only in monoMode */\r
2848 /* Note that this code is largely duplicated in woptions.c,\r
2849    function DrawSampleSquare, so that needs to be updated too */\r
2850 VOID\r
2851 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2852 {\r
2853   HBITMAP oldBitmap;\r
2854   HBRUSH oldBrush;\r
2855   int tmpSize;\r
2856 \r
2857   if (appData.blindfold) return;\r
2858 \r
2859   /* [AS] Use font-based pieces if needed */\r
2860   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2861     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2862     CreatePiecesFromFont();\r
2863 \r
2864     if( fontBitmapSquareSize == squareSize ) {\r
2865         int index = TranslatePieceToFontPiece(piece);\r
2866 \r
2867         SelectObject( tmphdc, hPieceMask[ index ] );\r
2868 \r
2869       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2870         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2871       else\r
2872         BitBlt( hdc,\r
2873             x, y,\r
2874             squareSize, squareSize,\r
2875             tmphdc,\r
2876             0, 0,\r
2877             SRCAND );\r
2878 \r
2879         SelectObject( tmphdc, hPieceFace[ index ] );\r
2880 \r
2881       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2882         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2883       else\r
2884         BitBlt( hdc,\r
2885             x, y,\r
2886             squareSize, squareSize,\r
2887             tmphdc,\r
2888             0, 0,\r
2889             SRCPAINT );\r
2890 \r
2891         return;\r
2892     }\r
2893   }\r
2894 \r
2895   if (appData.monoMode) {\r
2896     SelectObject(tmphdc, PieceBitmap(piece, \r
2897       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2898     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2899            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2900   } else {\r
2901     tmpSize = squareSize;\r
2902     if(minorSize &&\r
2903         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2904          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2905       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2906       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2907       x += (squareSize - minorSize)>>1;\r
2908       y += squareSize - minorSize - 2;\r
2909       tmpSize = minorSize;\r
2910     }\r
2911     if (color || appData.allWhite ) {\r
2912       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2913       if( color )\r
2914               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2915       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2916       if(appData.upsideDown && color==flipView)\r
2917         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2918       else\r
2919         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2920       /* Use black for outline of white pieces */\r
2921       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2922       if(appData.upsideDown && color==flipView)\r
2923         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2924       else\r
2925         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2926     } else {\r
2927       /* Use square color for details of black pieces */\r
2928       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2929       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2930       if(appData.upsideDown && !flipView)\r
2931         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2932       else\r
2933         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2934     }\r
2935     SelectObject(hdc, oldBrush);\r
2936     SelectObject(tmphdc, oldBitmap);\r
2937   }\r
2938 }\r
2939 \r
2940 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2941 int GetBackTextureMode( int algo )\r
2942 {\r
2943     int result = BACK_TEXTURE_MODE_DISABLED;\r
2944 \r
2945     switch( algo ) \r
2946     {\r
2947         case BACK_TEXTURE_MODE_PLAIN:\r
2948             result = 1; /* Always use identity map */\r
2949             break;\r
2950         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2951             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2952             break;\r
2953     }\r
2954 \r
2955     return result;\r
2956 }\r
2957 \r
2958 /* \r
2959     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2960     to handle redraws cleanly (as random numbers would always be different).\r
2961 */\r
2962 VOID RebuildTextureSquareInfo()\r
2963 {\r
2964     BITMAP bi;\r
2965     int lite_w = 0;\r
2966     int lite_h = 0;\r
2967     int dark_w = 0;\r
2968     int dark_h = 0;\r
2969     int row;\r
2970     int col;\r
2971 \r
2972     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2973 \r
2974     if( liteBackTexture != NULL ) {\r
2975         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2976             lite_w = bi.bmWidth;\r
2977             lite_h = bi.bmHeight;\r
2978         }\r
2979     }\r
2980 \r
2981     if( darkBackTexture != NULL ) {\r
2982         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2983             dark_w = bi.bmWidth;\r
2984             dark_h = bi.bmHeight;\r
2985         }\r
2986     }\r
2987 \r
2988     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2989         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2990             if( (col + row) & 1 ) {\r
2991                 /* Lite square */\r
2992                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2993                   if( lite_w >= squareSize*BOARD_WIDTH )\r
2994                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
2995                   else\r
2996                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2997                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
2998                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
2999                   else\r
3000                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3001                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3002                 }\r
3003             }\r
3004             else {\r
3005                 /* Dark square */\r
3006                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3007                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3008                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3009                   else\r
3010                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3011                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3012                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3013                   else\r
3014                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3015                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3016                 }\r
3017             }\r
3018         }\r
3019     }\r
3020 }\r
3021 \r
3022 /* [AS] Arrow highlighting support */\r
3023 \r
3024 static double A_WIDTH = 5; /* Width of arrow body */\r
3025 \r
3026 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3027 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3028 \r
3029 static double Sqr( double x )\r
3030 {\r
3031     return x*x;\r
3032 }\r
3033 \r
3034 static int Round( double x )\r
3035 {\r
3036     return (int) (x + 0.5);\r
3037 }\r
3038 \r
3039 /* Draw an arrow between two points using current settings */\r
3040 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3041 {\r
3042     POINT arrow[7];\r
3043     double dx, dy, j, k, x, y;\r
3044 \r
3045     if( d_x == s_x ) {\r
3046         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3047 \r
3048         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3049         arrow[0].y = s_y;\r
3050 \r
3051         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3052         arrow[1].y = d_y - h;\r
3053 \r
3054         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3055         arrow[2].y = d_y - h;\r
3056 \r
3057         arrow[3].x = d_x;\r
3058         arrow[3].y = d_y;\r
3059 \r
3060         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3061         arrow[5].y = d_y - h;\r
3062 \r
3063         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3064         arrow[4].y = d_y - h;\r
3065 \r
3066         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3067         arrow[6].y = s_y;\r
3068     }\r
3069     else if( d_y == s_y ) {\r
3070         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3071 \r
3072         arrow[0].x = s_x;\r
3073         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3074 \r
3075         arrow[1].x = d_x - w;\r
3076         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3077 \r
3078         arrow[2].x = d_x - w;\r
3079         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3080 \r
3081         arrow[3].x = d_x;\r
3082         arrow[3].y = d_y;\r
3083 \r
3084         arrow[5].x = d_x - w;\r
3085         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3086 \r
3087         arrow[4].x = d_x - w;\r
3088         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3089 \r
3090         arrow[6].x = s_x;\r
3091         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3092     }\r
3093     else {\r
3094         /* [AS] Needed a lot of paper for this! :-) */\r
3095         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3096         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3097   \r
3098         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3099 \r
3100         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3101 \r
3102         x = s_x;\r
3103         y = s_y;\r
3104 \r
3105         arrow[0].x = Round(x - j);\r
3106         arrow[0].y = Round(y + j*dx);\r
3107 \r
3108         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3109         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3110 \r
3111         if( d_x > s_x ) {\r
3112             x = (double) d_x - k;\r
3113             y = (double) d_y - k*dy;\r
3114         }\r
3115         else {\r
3116             x = (double) d_x + k;\r
3117             y = (double) d_y + k*dy;\r
3118         }\r
3119 \r
3120         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3121 \r
3122         arrow[6].x = Round(x - j);\r
3123         arrow[6].y = Round(y + j*dx);\r
3124 \r
3125         arrow[2].x = Round(arrow[6].x + 2*j);\r
3126         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3127 \r
3128         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3129         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3130 \r
3131         arrow[4].x = d_x;\r
3132         arrow[4].y = d_y;\r
3133 \r
3134         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3135         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3136     }\r
3137 \r
3138     Polygon( hdc, arrow, 7 );\r
3139 }\r
3140 \r
3141 /* [AS] Draw an arrow between two squares */\r
3142 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3143 {\r
3144     int s_x, s_y, d_x, d_y;\r
3145     HPEN hpen;\r
3146     HPEN holdpen;\r
3147     HBRUSH hbrush;\r
3148     HBRUSH holdbrush;\r
3149     LOGBRUSH stLB;\r
3150 \r
3151     if( s_col == d_col && s_row == d_row ) {\r
3152         return;\r
3153     }\r
3154 \r
3155     /* Get source and destination points */\r
3156     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3157     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3158 \r
3159     if( d_y > s_y ) {\r
3160         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3161     }\r
3162     else if( d_y < s_y ) {\r
3163         d_y += squareSize / 2 + squareSize / 4;\r
3164     }\r
3165     else {\r
3166         d_y += squareSize / 2;\r
3167     }\r
3168 \r
3169     if( d_x > s_x ) {\r
3170         d_x += squareSize / 2 - squareSize / 4;\r
3171     }\r
3172     else if( d_x < s_x ) {\r
3173         d_x += squareSize / 2 + squareSize / 4;\r
3174     }\r
3175     else {\r
3176         d_x += squareSize / 2;\r
3177     }\r
3178 \r
3179     s_x += squareSize / 2;\r
3180     s_y += squareSize / 2;\r
3181 \r
3182     /* Adjust width */\r
3183     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3184 \r
3185     /* Draw */\r
3186     stLB.lbStyle = BS_SOLID;\r
3187     stLB.lbColor = appData.highlightArrowColor;\r
3188     stLB.lbHatch = 0;\r
3189 \r
3190     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3191     holdpen = SelectObject( hdc, hpen );\r
3192     hbrush = CreateBrushIndirect( &stLB );\r
3193     holdbrush = SelectObject( hdc, hbrush );\r
3194 \r
3195     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3196 \r
3197     SelectObject( hdc, holdpen );\r
3198     SelectObject( hdc, holdbrush );\r
3199     DeleteObject( hpen );\r
3200     DeleteObject( hbrush );\r
3201 }\r
3202 \r
3203 BOOL HasHighlightInfo()\r
3204 {\r
3205     BOOL result = FALSE;\r
3206 \r
3207     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3208         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3209     {\r
3210         result = TRUE;\r
3211     }\r
3212 \r
3213     return result;\r
3214 }\r
3215 \r
3216 BOOL IsDrawArrowEnabled()\r
3217 {\r
3218     BOOL result = FALSE;\r
3219 \r
3220     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3221         result = TRUE;\r
3222     }\r
3223 \r
3224     return result;\r
3225 }\r
3226 \r
3227 VOID DrawArrowHighlight( HDC hdc )\r
3228 {\r
3229     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3230         DrawArrowBetweenSquares( hdc,\r
3231             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3232             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3233     }\r
3234 }\r
3235 \r
3236 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3237 {\r
3238     HRGN result = NULL;\r
3239 \r
3240     if( HasHighlightInfo() ) {\r
3241         int x1, y1, x2, y2;\r
3242         int sx, sy, dx, dy;\r
3243 \r
3244         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3245         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3246 \r
3247         sx = MIN( x1, x2 );\r
3248         sy = MIN( y1, y2 );\r
3249         dx = MAX( x1, x2 ) + squareSize;\r
3250         dy = MAX( y1, y2 ) + squareSize;\r
3251 \r
3252         result = CreateRectRgn( sx, sy, dx, dy );\r
3253     }\r
3254 \r
3255     return result;\r
3256 }\r
3257 \r
3258 /*\r
3259     Warning: this function modifies the behavior of several other functions. \r
3260     \r
3261     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3262     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3263     repaint is scattered all over the place, which is not good for features such as\r
3264     "arrow highlighting" that require a full repaint of the board.\r
3265 \r
3266     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3267     user interaction, when speed is not so important) but especially to avoid errors\r
3268     in the displayed graphics.\r
3269 \r
3270     In such patched places, I always try refer to this function so there is a single\r
3271     place to maintain knowledge.\r
3272     \r
3273     To restore the original behavior, just return FALSE unconditionally.\r
3274 */\r
3275 BOOL IsFullRepaintPreferrable()\r
3276 {\r
3277     BOOL result = FALSE;\r
3278 \r
3279     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3280         /* Arrow may appear on the board */\r
3281         result = TRUE;\r
3282     }\r
3283 \r
3284     return result;\r
3285 }\r
3286 \r
3287 /* \r
3288     This function is called by DrawPosition to know whether a full repaint must\r
3289     be forced or not.\r
3290 \r
3291     Only DrawPosition may directly call this function, which makes use of \r
3292     some state information. Other function should call DrawPosition specifying \r
3293     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3294 */\r
3295 BOOL DrawPositionNeedsFullRepaint()\r
3296 {\r
3297     BOOL result = FALSE;\r
3298 \r
3299     /* \r
3300         Probably a slightly better policy would be to trigger a full repaint\r
3301         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3302         but animation is fast enough that it's difficult to notice.\r
3303     */\r
3304     if( animInfo.piece == EmptySquare ) {\r
3305         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3306             result = TRUE;\r
3307         }\r
3308     }\r
3309 \r
3310     return result;\r
3311 }\r
3312 \r
3313 VOID\r
3314 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3315 {\r
3316   int row, column, x, y, square_color, piece_color;\r
3317   ChessSquare piece;\r
3318   HBRUSH oldBrush;\r
3319   HDC texture_hdc = NULL;\r
3320 \r
3321   /* [AS] Initialize background textures if needed */\r
3322   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3323       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3324       if( backTextureSquareSize != squareSize \r
3325        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3326           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3327           backTextureSquareSize = squareSize;\r
3328           RebuildTextureSquareInfo();\r
3329       }\r
3330 \r
3331       texture_hdc = CreateCompatibleDC( hdc );\r
3332   }\r
3333 \r
3334   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3335     for (column = 0; column < BOARD_WIDTH; column++) {\r
3336   \r
3337       SquareToPos(row, column, &x, &y);\r
3338 \r
3339       piece = board[row][column];\r
3340 \r
3341       square_color = ((column + row) % 2) == 1;\r
3342       if( gameInfo.variant == VariantXiangqi ) {\r
3343           square_color = !InPalace(row, column);\r
3344           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3345           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3346       }\r
3347       piece_color = (int) piece < (int) BlackPawn;\r
3348 \r
3349 \r
3350       /* [HGM] holdings file: light square or black */\r
3351       if(column == BOARD_LEFT-2) {\r
3352             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3353                 square_color = 1;\r
3354             else {\r
3355                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3356                 continue;\r
3357             }\r
3358       } else\r
3359       if(column == BOARD_RGHT + 1 ) {\r
3360             if( row < gameInfo.holdingsSize )\r
3361                 square_color = 1;\r
3362             else {\r
3363                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3364                 continue;\r
3365             }\r
3366       }\r
3367       if(column == BOARD_LEFT-1 ) /* left align */\r
3368             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3369       else if( column == BOARD_RGHT) /* right align */\r
3370             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3371       else\r
3372       if (appData.monoMode) {\r
3373         if (piece == EmptySquare) {\r
3374           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3375                  square_color ? WHITENESS : BLACKNESS);\r
3376         } else {\r
3377           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3378         }\r
3379       } \r
3380       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3381           /* [AS] Draw the square using a texture bitmap */\r
3382           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3383           int r = row, c = column; // [HGM] do not flip board in flipView\r
3384           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3385 \r
3386           DrawTile( x, y, \r
3387               squareSize, squareSize, \r
3388               hdc, \r
3389               texture_hdc,\r
3390               backTextureSquareInfo[r][c].mode,\r
3391               backTextureSquareInfo[r][c].x,\r
3392               backTextureSquareInfo[r][c].y );\r
3393 \r
3394           SelectObject( texture_hdc, hbm );\r
3395 \r
3396           if (piece != EmptySquare) {\r
3397               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3398           }\r
3399       }\r
3400       else {\r
3401         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3402 \r
3403         oldBrush = SelectObject(hdc, brush );\r
3404         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3405         SelectObject(hdc, oldBrush);\r
3406         if (piece != EmptySquare)\r
3407           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3408       }\r
3409     }\r
3410   }\r
3411 \r
3412   if( texture_hdc != NULL ) {\r
3413     DeleteDC( texture_hdc );\r
3414   }\r
3415 }\r
3416 \r
3417 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3418 void fputDW(FILE *f, int x)\r
3419 {\r
3420         fputc(x     & 255, f);\r
3421         fputc(x>>8  & 255, f);\r
3422         fputc(x>>16 & 255, f);\r
3423         fputc(x>>24 & 255, f);\r
3424 }\r
3425 \r
3426 #define MAX_CLIPS 200   /* more than enough */\r
3427 \r
3428 VOID\r
3429 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3430 {\r
3431 //  HBITMAP bufferBitmap;\r
3432   BITMAP bi;\r
3433 //  RECT Rect;\r
3434   HDC tmphdc;\r
3435   HBITMAP hbm;\r
3436   int w = 100, h = 50;\r
3437 \r
3438   if(logo == NULL) {\r
3439     if(!logoHeight) return;\r
3440     FillRect( hdc, &logoRect, whitePieceBrush );\r
3441   }\r
3442 //  GetClientRect(hwndMain, &Rect);\r
3443 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3444 //                                      Rect.bottom-Rect.top+1);\r
3445   tmphdc = CreateCompatibleDC(hdc);\r
3446   hbm = SelectObject(tmphdc, logo);\r
3447   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3448             w = bi.bmWidth;\r
3449             h = bi.bmHeight;\r
3450   }\r
3451   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3452                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3453   SelectObject(tmphdc, hbm);\r
3454   DeleteDC(tmphdc);\r
3455 }\r
3456 \r
3457 VOID\r
3458 DisplayLogos()\r
3459 {\r
3460   if(logoHeight) {\r
3461         HDC hdc = GetDC(hwndMain);\r
3462         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3463         if(appData.autoLogo) {\r
3464           \r
3465           switch(gameMode) { // pick logos based on game mode\r
3466             case IcsObserving:\r
3467                 whiteLogo = second.programLogo; // ICS logo\r
3468                 blackLogo = second.programLogo;\r
3469             default:\r
3470                 break;\r
3471             case IcsPlayingWhite:\r
3472                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3473                 blackLogo = second.programLogo; // ICS logo\r
3474                 break;\r
3475             case IcsPlayingBlack:\r
3476                 whiteLogo = second.programLogo; // ICS logo\r
3477                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3478                 break;\r
3479             case TwoMachinesPlay:\r
3480                 if(first.twoMachinesColor[0] == 'b') {\r
3481                     whiteLogo = second.programLogo;\r
3482                     blackLogo = first.programLogo;\r
3483                 }\r
3484                 break;\r
3485             case MachinePlaysWhite:\r
3486                 blackLogo = userLogo;\r
3487                 break;\r
3488             case MachinePlaysBlack:\r
3489                 whiteLogo = userLogo;\r
3490                 blackLogo = first.programLogo;\r
3491           }\r
3492         }\r
3493         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3494         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3495         ReleaseDC(hwndMain, hdc);\r
3496   }\r
3497 }\r
3498 \r
3499 void\r
3500 UpdateLogos(int display)\r
3501 { // called after loading new engine(s), in tourney or from menu\r
3502   LoadLogo(&first, 0, FALSE);\r
3503   LoadLogo(&second, 1, appData.icsActive);\r
3504   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3505   if(display) DisplayLogos();\r
3506 }\r
3507 \r
3508 static HDC hdcSeek;\r
3509 \r
3510 // [HGM] seekgraph\r
3511 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3512 {\r
3513     POINT stPt;\r
3514     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3515     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3516     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3517     SelectObject( hdcSeek, hp );\r
3518 }\r
3519 \r
3520 // front-end wrapper for drawing functions to do rectangles\r
3521 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3522 {\r
3523     HPEN hp;\r
3524     RECT rc;\r
3525 \r
3526     if (hdcSeek == NULL) {\r
3527     hdcSeek = GetDC(hwndMain);\r
3528       if (!appData.monoMode) {\r
3529         SelectPalette(hdcSeek, hPal, FALSE);\r
3530         RealizePalette(hdcSeek);\r
3531       }\r
3532     }\r
3533     hp = SelectObject( hdcSeek, gridPen );\r
3534     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3535     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3536     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3537     SelectObject( hdcSeek, hp );\r
3538 }\r
3539 \r
3540 // front-end wrapper for putting text in graph\r
3541 void DrawSeekText(char *buf, int x, int y)\r
3542 {\r
3543         SIZE stSize;\r
3544         SetBkMode( hdcSeek, TRANSPARENT );\r
3545         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3546         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3547 }\r
3548 \r
3549 void DrawSeekDot(int x, int y, int color)\r
3550 {\r
3551         int square = color & 0x80;\r
3552         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3553                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3554         color &= 0x7F;\r
3555         if(square)\r
3556             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3557                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3558         else\r
3559             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3560                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3561             SelectObject(hdcSeek, oldBrush);\r
3562 }\r
3563 \r
3564 void DrawSeekOpen()\r
3565 {\r
3566 }\r
3567 \r
3568 void DrawSeekClose()\r
3569 {\r
3570 }\r
3571 \r
3572 VOID\r
3573 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3574 {\r
3575   static Board lastReq[2], lastDrawn[2];\r
3576   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3577   static int lastDrawnFlipView = 0;\r
3578   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3579   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3580   HDC tmphdc;\r
3581   HDC hdcmem;\r
3582   HBITMAP bufferBitmap;\r
3583   HBITMAP oldBitmap;\r
3584   RECT Rect;\r
3585   HRGN clips[MAX_CLIPS];\r
3586   ChessSquare dragged_piece = EmptySquare;\r
3587   int nr = twoBoards*partnerUp;\r
3588 \r
3589   /* I'm undecided on this - this function figures out whether a full\r
3590    * repaint is necessary on its own, so there's no real reason to have the\r
3591    * caller tell it that.  I think this can safely be set to FALSE - but\r
3592    * if we trust the callers not to request full repaints unnessesarily, then\r
3593    * we could skip some clipping work.  In other words, only request a full\r
3594    * redraw when the majority of pieces have changed positions (ie. flip, \r
3595    * gamestart and similar)  --Hawk\r
3596    */\r
3597   Boolean fullrepaint = repaint;\r
3598 \r
3599   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3600 \r
3601   if( DrawPositionNeedsFullRepaint() ) {\r
3602       fullrepaint = TRUE;\r
3603   }\r
3604 \r
3605   if (board == NULL) {\r
3606     if (!lastReqValid[nr]) {\r
3607       return;\r
3608     }\r
3609     board = lastReq[nr];\r
3610   } else {\r
3611     CopyBoard(lastReq[nr], board);\r
3612     lastReqValid[nr] = 1;\r
3613   }\r
3614 \r
3615   if (doingSizing) {\r
3616     return;\r
3617   }\r
3618 \r
3619   if (IsIconic(hwndMain)) {\r
3620     return;\r
3621   }\r
3622 \r
3623   if (hdc == NULL) {\r
3624     hdc = GetDC(hwndMain);\r
3625     if (!appData.monoMode) {\r
3626       SelectPalette(hdc, hPal, FALSE);\r
3627       RealizePalette(hdc);\r
3628     }\r
3629     releaseDC = TRUE;\r
3630   } else {\r
3631     releaseDC = FALSE;\r
3632   }\r
3633 \r
3634   /* Create some work-DCs */\r
3635   hdcmem = CreateCompatibleDC(hdc);\r
3636   tmphdc = CreateCompatibleDC(hdc);\r
3637 \r
3638   /* If dragging is in progress, we temporarely remove the piece */\r
3639   /* [HGM] or temporarily decrease count if stacked              */\r
3640   /*       !! Moved to before board compare !!                   */\r
3641   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3642     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3643     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3644             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3645         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3646     } else \r
3647     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3648             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3649         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3650     } else \r
3651         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3652   }\r
3653 \r
3654   /* Figure out which squares need updating by comparing the \r
3655    * newest board with the last drawn board and checking if\r
3656    * flipping has changed.\r
3657    */\r
3658   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3659     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3660       for (column = 0; column < BOARD_WIDTH; column++) {\r
3661         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3662           SquareToPos(row, column, &x, &y);\r
3663           clips[num_clips++] =\r
3664             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3665         }\r
3666       }\r
3667     }\r
3668    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3669     for (i=0; i<2; i++) {\r
3670       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3671           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3672         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3673             lastDrawnHighlight.sq[i].y >= 0) {\r
3674           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3675                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3676           clips[num_clips++] =\r
3677