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