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