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