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