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